关于原生SQL语句的那些事儿

166 阅读6分钟
本文已参与「新人创作礼」活动,一起开启掘金创作之路。
关于原生SQL语句的那些事儿
语句中各子语句的执行顺序
  1. from(也包括from中的子语句)
  2. join
  3. on
  4. where
  5. group by(这里开始使用select中的别名,后面的语句中都可以使用)
  6. avg,sum .... (聚合函数)
  7. having
  8. select
  9. distinct
  10. order by
  11. limit
执行顺序分析

所有的 查询语句都是从from开始执行的,在执行过程中,每个步骤都会为下一个步骤生成一个虚拟表,这个虚拟表将作为下一个执行步骤的输入。

from

form是一次查询语句的开端。

如果是一张表,会直接操作这张表; 如果这个from后面是一个子查询,会先执行子查询中的内容,子查询的结果也就是第一个虚拟表T1。(注意:子查询中的执行流程也是按照本篇文章讲的顺序哦)。 如果需要关联表,使用join

join

如果from后面是多张表,join关联,会首先对前两个表执行一个笛卡尔乘积,这时候就会生成第一个虚拟表T1(注意:这里会选择相对小的表作为基础表);

on

对虚表T1进行ON筛选,只有那些符合的行才会被记录在虚表T2中。(注意,这里的这里如果还有第三个表与之关联,会用T2与第三个表进行笛卡尔乘积生产T3表,继续重复3. on步骤生成T4表,不过下面的顺序讲解暂时不针对这里的T3和T4,只是从一个表关联查询T2继续说)

where

对虚拟表T2进行WHERE条件过滤。只有符合的记录才会被插入到虚拟表T3中。

group by

group by 子句将中的唯一的值组合成为一组,得到虚拟表T4。如果应用了group by,那么后面的所有步骤都只能操作T4的列或者是执行6.聚合函数(count、sum、avg等)。(注意:原因在于分组后最终的结果集中只包含每个组中的一行。谨记,不然这里会出现很多问题,下面的代码误区会特别说。)

avg,sum… 等聚合函数

聚合函数只是对分组的结果进行一些处理,拿到某些想要的聚合值,例如求和,统计数量等,并不生成虚拟表。

having

应用having筛选器,生成T5。HAVING子句主要和GROUP BY子句配合使用,having筛选器是第一个也是为唯一一个应用到已分组数据的筛选器。

select

执行select操作,选择指定的列,插入到虚拟表T6中。

distinct

对T6中的记录进行去重。移除相同的行,产生虚拟表T7.(注意:事实上如果应用了group by子句那么distinct是多余的,原因同样在于,分组的时候是将列中唯一的值分成一组,同时只为每一组返回一行记录,那么所以的记录都将是不相同的。 )

order by

应用order by子句。按照order_by_condition排序T7,此时返回的一个游标,而不是虚拟表。sql是基于集合的理论的,集合不会预先对他的行排序,它只是成员的逻辑集合,成员的顺序是无关紧要的。对表进行排序的查询可以返回一个对象,这个对象包含特定的物理顺序的逻辑组织。这个对象就叫游标。 oder by的几点说明

因为order by返回值是游标,那么使用order by 子句查询不能应用于表表达式。 order by排序是很需要成本的,除非你必须要排序,否则最好不要指定order by, order by的两个参数 asc(升序排列) desc(降序排列)

limit

取出指定行的记录,产生虚拟表T9, 并将结果返回。

limit后面的参数可以是 一个limit m ,也可以是limit m n,表示从第m条到第n条数据。

(注意:很多开发人员喜欢使用该语句来解决分页问题。对于小数据,使用LIMIT子句没有任何问题,当数据量非常大的时候,使用LIMIT n, m是非常低效的。因为LIMIT的机制是每次都是从头开始扫描,如果需要从第60万行开始,读取3条数据,就需要先扫描定位到60万行,然后再进行读取,而扫描的过程是一个非常低效的过程。所以,对于大数据处理时,是非常有必要在应用层建立一定的缓存机制)

书写SQL语句注意事项

书写规范上的注意

  • 字符串类型的要加单引号
  • select后面的每个字段要用逗号分隔,但是最后连着from的字段不要加逗号
  • 使用子查询创建临时表的时候要使用别名,否则会报错。

增强性能

  • 不要使select * from ……返回所有列,只检索需要的列,可避免后续因表结构变化导致的不必要的程序修改,还可降低额外消耗的资源
  • 不要检索已知的列 select user_id,name from User where user_id = ‘10000050’
  • 使用可参数化的搜索条件,如=, >, >=, <, <=,between, in, is null以及like ‘%’;尽量不要使用非参数化的负向查询,这将导致无法使用索引,如<>, !=, !>,!<, not in, not like,not exists, not between, is not null,like ‘%’
  • 当需要验证是否有符合条件的记录时,使用exists,不要使用count(*),前者在第一个匹配记录处返回,后者需要遍历所有匹配记录
  • Where子句中列的顺序与需使用的索引顺序保持一致,不是所有数据库的优化器都能对此顺序进行优化,保持良好编程习惯(索引相关)
  • 不要在where子句中对字段进行运算或函数(索引相关)
  • 如where amount / 2 > 100,即使amount字段有索引,也无法使用,改成where amount > 100 * 2就可使用amount列上的索引
  • 如where substring( Lastname, 1, 1) = ‘F’就无法使用Lastname列上的索引,而where Lastname like ‘F%’或者where Lastname >= ‘F’ and Lastname < ‘G’就可以
  • 在有min、max、distinct、order by、group by操作的列上建索引,避免额外的排序开销(索引相关)
  • 小心使用or操作,and操作中任何一个子句可使用索引都会提高查询性能,但是or条件中任何一个不能使用索引,都将导致查询性能下降,如where member_no = 1 or provider_no = 1,在member_no或provider_no任何一个字段上没有索引,都将导致表扫描或聚簇索引扫描(索引相关)
  • Between一般比in/or高效得多,如果能在between和in/or条件中选择,那么始终选择between条件,并用>=和<=条件组合替代between子句,因为不是所有数据库的优化器都能把between子句改写为>=和<=条件组合,如果不能改写将导致无法使用索引(索引相关)
  • 调整join操作顺序以使性能最优,join操作是自顶向下的,尽量把结果集小的两个表关联放在前面,可提高性能。(join相关)