结合业务,精炼SQL

  现代网站,性能的瓶颈都围绕着数据库的性能来谈。数据库是存储的核心部件,在日益增长的流量中会凸显数据库的性能瓶颈。从《淘宝技术十年》书中来看,淘宝发展历程中从MYSQL换成了ORACLE又换成了MYSQL集群,每次变换数据库都是因为业务的增长,使得数据库的读写效率达到了瓶颈。作为互联网公司最受欢迎的数据库MYSQL,它有自己独特的魅力:轻便、社区版免费、可做集群等,即便是它拥有这么多特性和优势,我们开发人员也只是觉得它的优势只有:便于使用与学习。

  如果不是身为架构师的开发人员,最经常写到的就是SQL了,一条好的SQL抵得过几百行优化代码。因为程序的各处都在执行SQL,数据库如果执行太多的复杂的SQL,则它可能会卡死,这就意味着你的系统会死掉,有可能丢失大量用户的现在操作,这是灾难性的:别人觉得你的软件不好,丢失了客户。所以,掌握高效SQL的技巧是开发人员的必修课。(虽然我觉得我每天都在写SQL,但是其实在写EXCEL)

  持久层存储大概有内存和硬盘两种(我自己分的),内存级存储严格来说不算持久化存储,它们大体上使用的是NOSQL,存储的格式是BigTable,key,value型存储,存储的数据为非关系型的,现有的产品是Memcached,Redis,MongoDB。推测是使用哈希表数据结构。而硬盘级别的我认为属于严格的持久化存储了,它们使用结构化查询语言SQL,存储的数据为关系型的,现有的产品为MYSQL,ORACLE,DB2等等。

  总之,业务程序员(没有贬低,233)写业务的时候,基本上一个需求就是一个数据查询任务,写出慢的SQL,自然有让它变快的能力。唯一的原则就是:不要让数据库做多余的事情

  • CRUD,日常操作

  作为一个excel工作者(呸),当然是开发出可以供用户使用的excel页面啦。举个栗子,比如用户管理里面的,按不同的条件来查询用户,以及和另一张表共同过滤某些用户数据等。当你的系统做大了,你需要分析用户的行为以促进你的网站业务的增长,比如某种特性的用户(比如购买某种VIP)增长得很快,那么你需要去查询他们最多购买的产品、感兴趣的产品、以及消费区间等等。这其实都是查询语句的应用场景,而且在互联网企业中,查的操作是十分频繁的。

  对于SQL的语句来一个范式,那大概是:insert/delete/select/update 字段操作 from 表 where 筛选条件。虽然字段操作部分各有区别,但是重点在于where子句的编写,因为它的编写决定你SQL的执行时间。

  insert

insert into 表名(字段) values(字段值)

  写操作的执行应该是不算频繁的,对于大多数CRM或者ERP来说,这条语句已经足够了。如果插入多条,那就执行多次吧。

  delete

delete from 表名 where 条件

  删除操作是一种微妙复杂的操作,它不会影响系统性能,但是多表关联的时候,删除需要格外小心,程序中有可能根据某表的某些字段去查这个表的某列数据,你删除掉了,就可能报空异常。所以在设计表和程序的时候,需要考虑一种弱耦合的策略。

  update

update 表名 set 字段1=值1,字段2=值2... where 筛选条件

  更新操作也是一种微妙复杂的操作,系统性能的影响是有可能的,如果高并发写操作,由于博主缺少经验,不是很能想象这种场景处理方式(12306好像就是高读写)。更新的关键是,关联数据字段id不能随意更改,类似于指针丢失的概念。

  select

select 字段名 from 表名 where 筛选条件

  查操作十分的花式,你的产品经理会让你各种方式去筛选数据,因为互联网公司比较在意的是某些条件的数据的价值。再举个栗子,我们需要知道哪些用户是付费的、哪些用户是免费的、那些用户是忠实的、那些用户是活跃的等等,这些条件导致where字句变化多端。在系统有十分多查操作的时候,你的数据量很大,select语句有可能就会卡死数据库。所以,在写select的时候,把需要的字段拿出来就好了,避免写select *,如果你经常select *,那我不知道你会不会被主管打死。(其实我设想,数据库在筛选数据的时候应该是根据where筛选,耗时的是where的计算,而select * 大概是消耗传输带宽,不知道设想得对不对)。提高查询效率大概就只有合理应用索引这个方法了。如果数据量再大,就分库分表什么的了。

  • 源数据的处理

  假设查询一张表,到前台时候需要把数据包装一下,在不需要二次查询的时候,不想让前台看到字段名为key的json数据,那么有可能需要遍历后重新put进Map中,这样就有些无谓操作了。

  1.as能帮到你

   如果你的表是,什么USERNAME,PASSWORD这样全大写的字段,那你在select的时候可以as 别名。

select USERNAME as username,PASSWORD as password,EMAIL as email from USER;

  如果不写as,后面跟着另一个字符串的话默认也会变成别名。别名的作用在联合查询中也很有用处。

select a.字段1 as 字段1别名,b.字段2 as 字段2别名 from 表A as a ,表B as b where 筛选条件

  联合查询中用别名来区分一个SQL中的字段是属于哪个表中的。

  2.活用常用数据库函数

  有些表需要记录最后操作时间,如果在服务器语言中执行插入时候获取当前时间,是不是感觉有些别扭。可以使用now()函数。数据库会将当前数据库时间作为datetime类型写入。

  像某些计算查到内存里计算显得有些别扭,所以记得使用sum()求和和avg()求平均值。(是不是真的像做excel一样)

  count()这个函数是十分常用了,计算总共多少条数据。

  max()min()也十分实用。

  Date_Format()日期转换,少了我在内存中转换的一步,拿来直接用,不过要记得这个返回的会是字符串类型,如果你要使用Date类型,那得在服务器中转换。

  cast(表达式 as decimal)算是很实用的取整函数了,有了它可以少一步在内存中转换。

  所以写一条SQL可以长得很恶心,为了从源数据中拿到马上能用的数据。

select sum(字段1) as 字段1别名,cast(avg(字段2) as decimal) as 字段2别名,Date_Format(字段3,"%Y-%m-%d") as 字段3别名 from 表名 where 筛选条件

  但是从程序上来说,拿到的数据会很爽。

  3.多条数据处理

  博主一度陷入多条数据不会处理的境地,比如一张订单表,需要你分页查询(按用户,按日期,都可以),这回需要什么鬼实现方式?刚开始我想的是查到内存里然后进行计算,某些数据被剔除掉,这会造成你limit语句是个假分页……也就是查出10条,过滤掉了一些,页面上每页显示都不到10条,并且参差不齐。

  这时候我想起大学时候某位前辈说的,一行SQL胜过千万行程序。使用group by就好了。

select sum(订单金额) ,用户编号  from 订单表 where 条件 group by 用户编号 limit n,m

  这样就是聚合翻页了,按照用户编号分组的。

  如果你要筛选订单金额大于某个值的数据呢?头疼,又去内存里遍历?使用having

sselect sum(订单金额) as 订单金额别名,用户编号  from 订单表 where 条件 group by 用户编号 having 订单金额别名 > a and 订单金额别名 < b limit n,m

  4.升序、降序

  小儿科,order by 某字段 (desc)。这个主要还有一个,比如我想计算用户的购买记录,需要带上时间的。

select sum(购买总额),时间 from 订单表 where 条件

  这样你出来的时间,会是最早那条。(反正不准)

  为了保证统计的总数的时候,同时拿到最新的那条购买时间,这么写就好了。

select sum(购买总额),max(时间) from 订单表 where 条件
  • 联合查询

  用得很少,因为现在的架构比较提倡单表弱耦合查询,因为联合查询的SQL很难读,并且一次join就要多算一个表,大概是连乘的复杂度。

  A left join B,左联合,表示以左表为基准,无论B有没有数据,A都会按照where条件查询完毕。结果相当于B集合除去A与B的交集。(图我就不画了)

  A right join B,右联合,表示以右表为基准。集合里相当是A除去A与B的交集。

  A inner join ,交集。

  join都可以后面加个on表示集合运算的条件。

   举个例子

select a.字段1,b.字段2 from 表A as a inner/left/right join 表B as b on(a.id=b.id)

  就是以a为中心,连接b表查询。

  在联合查询中,一般会以某表为中心表,散发式连接多表进行信息筛选。

  模拟一个业务环节,有一张用户表,用户会员信息表,用户订单表。可以查什么数据呢?这就很复杂了,比如查询还在会员期内的会员购买订单的金额、非会员的购买金额、未付款的订单金额等等。CRM系统的需求十分的变化多端,为的就是分析用户的行为从而增加软件的收入。

CREATE TABLE `USER` (
  `GMT_CREATE` datetime NOT NULL,
  `GMT_MODIFIED` datetime NOT NULL,
  `USER_ID` varchar(64) CHARACTER SET latin1 NOT NULL,
  `USER_NAME` varchar(64) CHARACTER SET latin1 DEFAULT NULL,
  `USER_PASSWORD` varchar(64) CHARACTER SET latin1 DEFAULT NULL,
  `USER_EMAIL` varchar(64) CHARACTER SET latin1 DEFAULT NULL,
  PRIMARY KEY (`USER_ID`),
  UNIQUE KEY `USER_ID_UNIQUE` (`USER_ID`),
  KEY `USER_ID` (`USER_ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `USER_MEMBER` (
  `GMT_CREATE` datetime DEFAULT NULL,
  `USER_ID` varchar(64) NOT NULL COMMENT ‘会员id‘,
  `MEMBER_START` datetime NOT NULL COMMENT ‘会员开始时间‘,
  `MEMBER_END` datetime NOT NULL COMMENT ‘会员结束时间‘,
  `MEMBER_RANK` varchar(16) DEFAULT NULL COMMENT ‘会员等级‘,
  PRIMARY KEY (`USER_ID`),
  UNIQUE KEY `USER_ID_UNIQUE` (`USER_ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT=‘会员信息‘;
CREATE TABLE `USER_ORDER` (
  `GMT_CREATE` datetime DEFAULT NULL,
  `ID` int(20) NOT NULL AUTO_INCREMENT,
  `USER_ID` varchar(64) DEFAULT NULL COMMENT ‘用户id‘,
  `ORDER_PRICE` varchar(45) DEFAULT NULL COMMENT ‘订单金额‘,
  `STATUS` varchar(45) DEFAULT NULL COMMENT ‘订单支付状态‘,
  PRIMARY KEY (`ID`),
  KEY `uid` (`USER_ID`)
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8 COMMENT=‘用户订单‘;

  

  添加了一些数据。

  举个例子,我想查在期内的用户消费量,说着简单,写着就复杂了。

select a.USER_ID ,a.USER_NAME ,sum(c.ORDER_PRICE) from USER as a inner join USER_MEMBER as b on(a.USER_ID = b.USER_ID and MEMBER_END > now()) inner join USER_ORDER as c on(a.USER_ID = c.USER_ID and c.STATUS = ‘yes‘) group by a.USER_ID

  

  • 结语

    经过实际开发的洗礼,深深的认识到写SQL的重要性,高效的SQL使得程序也变得简洁,对于SQL结果集的数据处理也是要小心的,一不注意就会报空指针异常。作为一名走向资深EXCEL开发的工程师来说(呸),确实,SQL是基础中的基础,在此做一个阶段性的总结。

时间: 2024-08-26 10:08:55

结合业务,精炼SQL的相关文章

Mysql 根据业务完善sql

1.将一个表的字段值更新到另外一张关联表的字段中 UPDATE ultrax_home_friend  friend INNER JOIN ultrax_common_member  member ON friend.fuid=member.uidSET friend.fusername=member.username

业务数据库开发上线流程v1.0

XX业务数据库开发上线流程v1.0 草拟时间:2015.11.23制订时间:修订时间: 0x00 目的 本文定义了业务在开发上线发布过程中涉及的数据库相关流程,指引业务人员高效完成数据库上线工作,最终确保上线质量可控. 0x01 适用范围 人员:所有研发人员,运维人员数据库:业务所有MySQL 数据库 0x02 流程说明 流程内容包含:数据库申请-->功能开发---功能与性能测试-->提交审核-->运维审核--->上线变更---验证---上线结束 1. 数据库服务申请 流程负责人员

配置SQL Server AlwaysOn

数据处理是企业绝大多数应用的核心,在生产环境中,数据库总是扮演着非常重要的角色.在微软的SQL Server中主要通过四个传统的高可用性和灾难恢复技术:故障转移群集(Cluster).日志传送(Log Shipping).复制(Replication).和数据库镜像(Database Mirroring)来保障业务的连续性.在SQL Server 2012中微软新增一种高可用性技术来保证生产环境下的业务连续性-SQL Server Always On. AlwaysOn可用性组功能是一个提供替代

数据库应用SQL编写规范

1. 编程规范 本章讲述ORACLE数据库SQL脚本编码规范. 1.1. 总述 1.1.1. 编码规范 1)编写脚本时,Oracle保留字大写,其他一律使用小写,除非必要(如:作字符串时或注释语句中): 2)脚本必须规范,SQL编写不能采用缩略写法:如INSERT INTO中的INTO不能省略:INTO后面要列出字段名: 3)语句内的空格,统一为一个空格: 4)表定义中,字段名.字段类型(长度).缺省.(非)空.字段注释都必须左对齐: 5)使用空格键对齐,不要使用Tab键: 6)运算符前后,各保

Hibernated的sql查询

记录一下学习Hibernate的心得 1.为什么HIbernate会支持原生态的sql查询? HQL查询语句虽然方便我们查询,但是基于HQL的查询会将查询出来的对象保存到hibernate的缓存当中,如果在我们的一个大型项目中(数据量超过了百万级),这个时候如果使用hibernate的HQL查询的话,会一次将我们查询的对象查询出来后放到缓存中,这个时候会影响我们的效率,所以当在大型项目中使用hibernate时我们的最佳实践就是--使用原生的SQL查询语句,因为通过SQL查询的话,是不会经过hi

初次使用SQL调优建议工具--SQL Tuning Advisor

在10g中,Oracle推出了自己的SQL优化辅助工具: SQL优化器(SQL Tuning Advisor :STA),它是新的DBMS_SQLTUNE包. 使用STA一定要保证优化器是CBO模式下.可是我觉得使用这样的工具,仅适合全然不懂SQL的调优的人群,不要觉得工具能解决好问题. SQL说究竟是表达的是一个业务,工具怎么可能理解业务.SQL调优还是要用autotrace,10046,10053,display_cursor这些优秀的工具做诊断.然后依据业务和所具备的oracle基础的知识

SQL Server 分布式事务与本地事务

SQL Server 分布式事务与本地事务 @(SQL Server) 背景:之前有项目中出现大量死锁,进行排查后最终发现很多死锁都是由于序列化隔离级别导致,开发针对业务和SQL进行优化后,死锁减少,但是没进行后续研究.最近又有很多项目出现死锁及超时,特别是工作流和待办这块,同样发现都是存在序列化,于是针对这一点进行相关资料查阅及解答. 一. 为什么会出现serializable(序列化) 如果我们程序中定义事务类调用了分布式事务,那么事务的隔离级别默认就是serializable,数据库中即会

如何构建一个flink sql平台

我们都知道,离线计算有Hive,使用过的知道,需要先定义一个schema,比如针对HDFS这种存储对标mysql定义一个schema,schema的本质是什么?主要描述下面这些信息 1)当前存储的物理位置的描述 2)数据格式的组成形式 然后Hive可以让用户定义一段sql,针对上面定义的schema进行,sql的本质是什么,是业务逻辑的描述.然后Hive内部会将这段sql进行编译转化为原生的底层MapReduce操作,通过这种方式,屏蔽底层技术原理,让业务开发人员集中精力在schema和sql业

个人永久性免费-Excel催化剂功能第21波-Excel与Sqlserver零门槛交互-执行SQL语句篇

在前两波中,已完成了Excel与Sqlserver的查询和上传功能,但难免许多临时的或更深入地操作数据库需要用Sql语句来操作,对一般用户电脑里,不可能有条件轻易安装一个数据库客户端软件,就算安装了对其中烦多的功能操作也不胜任. 开发一个简单的接受SQL语句对数据库进行访问操作就有点必要,当然这个落脚点放到Excel上是很不错的选择,毕竟所有用户电脑都有安装Excel. 并且在Excel上批量构造SQL语句也是容易的事,若有数据需要从数据库中导出,Excel作为装载小量数据并进行后续分析的容器是