SQL优化总结
SQL查询的逻辑执行顺序
SQL 不同于与其他编程语言的最明显特征是处理代码的顺序。在大数编程语言中,代码按编码顺序被处理,但是在SQL语言中,第一个被处理的子句是FROM子句,尽管SELECT语句第一个出现,但是几乎总是最后被处理。每个步骤都会产生一个虚拟表,该虚拟表被用作下一个步骤的输入。这些虚拟表对调用者(客户端应用程序或者外部查询)不可用。只是最后一步生成的表才会返回 给调用者。如果没有在查询中指定某一子句,将跳过相应的步骤。下面是对应用于SQL server的各个逻辑步骤的简单描述。
(8)SELECT (9)DISTINCT (11)<Top Num> <select list>
(1)FROM [left_table]
(3)<join_type> JOIN <right_table>
(2)ON <join_condition>
(4)WHERE <where_condition>
(5)GROUP BY <group_by_list>
(6)WITH <CUBE | RollUP>
(7)HAVING <having_condition>
(10)ORDER BY <order_by_list>
循环改批量
循环单条操作,请尽量改成批量操作:
- 每次for循环出来的结果放在一个集合里,然后在让SQL执行批量插入,不要找到一条就往数据库写一条。
- 数据库中的游标也是差不多的道理,如果有可能不用游标循环一条一条处理,请尽量不要使用。如果自己认为必须用,也请问问别人是否可以有其他方式做批量。
- 如果没法避免一条一条的写入,那么在处理前显示开启一个事务 begin tran 在处理完成后 commit 这样也要比不开显示事务会快很多。
降低语句复杂性
- 程序逻辑本身就很复杂,需要很多表连接,又要排序又要聚合,时不时来几个子查询,外加几个函数。
- 由于业务有很大的共性,所以创建出很多视图,甚至视图嵌套很多层视图,最后外层又要关联单个模块的特殊性表。
- 对于第一种情况,代码看起来就很长很复杂,看起来很牛逼的代码其实是很LOW的。而对于第二种,看起来代码很简洁,但经过SQL优化器的二次编译,其实和第一种并无区别。这两种的解决办法都是降低复杂性,把一些能拆分出来的尽量拆分出来放入临时表或者表变量中,比如先把条件筛选性较强的几张表关联,然后把结果放入临时表,在用临时表和其他表关联。可以理解成我有10张表关联,我先拿5张表出来关联,然后把结果放入临时表,再跟另外5张表关联。这样这个查询的复杂度由10张表的联合变成 5+6,这样降低了复杂语句复杂度。复杂视图也是如此,在视图和外层关联前,放入临时表,再跟外层关联。子查询也是如此,可以分离出来成为临时表的子查询,先分离出来。要学会恰当使用临时表。
索引注意事项
- 条件可以用索引但是索引列不能带函数,不能参与计算。如where productID/2 = @a ,不能有隐式转换等。
- 索引查找(seek),一般为最优(但查找也要看查找的筛选性),尽量吧where 条件中的字段建成一个组合索引,并且包含要查询select 中的字段。
要会看懂执行计划
通过执行计划可以看出语句的主要消耗到底在哪里,另外配合set statistics io on 等分析读次数,也是优化的关键,创建或优化索引页是主要从这里出发。
只返回需要的数据
返回数据到客户端至少需要数据库提取数据、网络传输数据、客户端接收数据以及客户端处理数据等环节,如果返回不需要的数据,就会增加服务器、网络和客户端的无效劳动,其害处是显而易见的,避免这类事件需要注意:
- 不要写SELECT * 的语句,而是选择你需要的字段。
- 当在SQL语句中连接多个表时, 请使用表的别名并把别名前缀于每个Column上.这样一来,就可以减少解析的时间并减少那些由Column歧义引起的语法错误。
- where 条件要尽量的多且保证高筛选性。
- 业务中很常见要返回大批量数据到前端,但是这些数据真的都是必要的么?前端是否可以加一些默认条件呢?
减少不必要的操作
- 写语句之前,要理清思路
- 杜绝不必要的表连接,多一个表链接代表多很大开销。
- 减少不必要的条件判断,很多时候前台传入为空值得时候 后台语句被写成XX=XX OR XX IS NULL OR XX LIKE OR ...OR ...OR 等。这是比较经典的问题了,请加入判断在拼入最后的条件。
- 你的语句需要去重复么? distinct 、union等操作。
- LEFT JOIN 和 inner join的区别,是否真的需要left join,否则选用inner join 来减少不必要的数据返回。
- order by 你的语句是否需要排序?排序是否可以通过索引来降低性能消耗? 像插入数据也带着order by的要好好反省下了。
尽量早的筛选
- 最经典的例子就是where 和 having的区别,看过语句执行顺序你应该已经明白了。能写在where 中不要放在having中。
- 使用临时表降低语句复杂性,要降低临时表的数据量,也就是要把有条件的表尽量关联并做成临时表。
- 隐式转换,索引字段使用计算或函数,也会导致数据不能尽早筛选。
常用的写法误区
- 避免使用 in、not in,数据量小的时候不会有问题,如果数据量大会严重影响性能。
- 事务操作过程要尽量小,能拆分的事务要拆分开来。
- 使用with(nolock)查询语句不会阻塞,一般情况下是这样,但是如果有架构修改或快照发布等使用with(nolock)也会阻塞。
- 尽量用exists 代替 in。