南顺网络

扫一扫微信二维码

值得收藏的52条提升性能的SQL优化策略

南顺网络 2021-10-10 08:49 经验之谈 0

SQL 语句性能优化策略

1、对查询进行优化,应尽量避免全表扫描,首先应考虑在 WHERE 及 ORDER BY 涉及的列上建立索引。

2、应尽量避免在 WHERE 子句中对字段进行 NULL 值判断,创建表时 NULL 是默认值,但大多数时候应该使用 NOT NULL,或者使用一个特殊的值,如 0 ,-1作为默认值。

3、应尽量避免在 WHERE 子句中使用 != 或 <> 操作符。MySQL 只有对以下操作符才使用索引:<,<=,=,>,>=,BETWEEN,IN,以及某些时候的 LIKE。

4、应尽量避免在 WHERE 子句中使用 OR 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描,可以使用 UNION 合并查询。

5、IN 和 NOT IN 也要慎用,否则会导致全表扫描。对于连续的数值,能用 BETWEEN 就不要用 IN。

6、下面的查询也将导致全表扫描:

1

select id from t where name like‘%abc%’//用到索引

或者

1

select id from t where name like‘%abc’//若要提高效率,可以考虑全文检索

7、如果在 WHERE 子句中使用参数,也会导致全表扫描。

8、应尽量避免在 WHERE 子句中对字段进行表达式操作和函数操作。

9、很多时候用 EXISTS 代替 IN 是一个好的选择。

10、索引固然可以提高相应的 SELECT 的效率,但同时也降低了 INSERT 及 UPDATE 。因为 INSERT 或 UPDATE 时有可能会重建索引,一个表的索引数最好不要超过 6 个。

11、应尽可能的避免更新 clustered 索引数据列, 因为 clustered 索引数据列的顺序就是表记录的物理存储顺序,一旦该列值改变将导致整个表记录的顺序的调整,会耗费相当大的资源。

12、尽量使用数字型,若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的性能,并会增加存储。

13、尽可能的使用 varchar, nvarchar 代替 char, nchar。因为首先长字段存储空间小,可以节省存储空间,对于查询来说,在一个相对较小的字段内搜索效率显然要高些。

14、最好不要使用返回所有:select from t ,用具体的字段列表代替 “*”,不要返回用不到的任何字段。

15、尽量避免向客户端返回大数据量,若数据量过大,应该考虑相应需求是否合理。

16、使用表的别名(Alias):当在 SQL 语句中连接多个表时,请使用表的别名并把别名前缀于每个 Column 上。这样一来,就可以减少解析时间并减少那些由 Column 歧义引起的语法错误。

17、使用“临时表”暂存中间结果 。

简化 SQL 语句的重要方法就是采用临时表暂存中间结果。将临时结果暂存在临时表,后面的查询就在 tempdb 中了,这可以避免程序中多次扫描主表,也大大减少了程序执行中“共享锁”阻塞“更新锁”,减少了阻塞,提高了并发性能。

18、一些 SQL 查询语句应加上 nolock,读、写是会相互阻塞的,为了提高并发性能。对于一些查询,可以加上 nolock,这样读的时候可以允许写,但缺点是可能读到未提交的脏数据。

使用 nolock 有3条原则:

查询的结果用于“插、删、改”的不能加 nolock;
查询的表属于频繁发生页分裂的,慎用 nolock ;

使用临时表一样可以保存“数据前影”,起到类似 Oracle 的 undo 表空间的功能,能采用临时表提高并发性能的,不要用 nolock。

19、常见的简化规则如下:不要有超过 5 个以上的表连接(JOIN),考虑使用临时表或表变量存放中间结果。少用子查询,视图嵌套不要过深,一般视图嵌套不要超过 2 个为宜。

20、将需要查询的结果预先计算好放在表中,查询的时候再Select。

21、用 OR 字句可以分解成多个查询,并且通过 UNION 连接多个查询。他们的速度与是否使用索引有关,如果查询需要用到联合索引,用 UNION all 执行的效率更高。多个 OR 的字句没有用到索引,改写成 UNION 的形式再试图与索引匹配。

22、在IN后面值的列表中,将出现最频繁的值放在最前面,出现得最少的放在最后面,减少判断次数。

23、尽量将数据的处理工作放在服务器上,如使用存储过程。存储过程是编译好、优化过、并且被组织到一个执行规划、且存储在数据库中的 SQL 语句,是控制流语言的集合,速度当然快。反复执行的动态 SQL,可以使用临时存储过程,该过程(临时表)被放在 Tempdb 中。

24、当服务器的内存够多时,配制线程数量 = 最大连接数+5,这样能发挥最大的效率;否则使用配制线程数量< 最大连接数,启用 SQL SERVER 的线程池来解决,如果还是数量 = 最大连接数+5,严重的损害服务器的性能。

25、查询的关联同写的顺序 :

1

2

3

select a.personMemberID, * from chineseresume a,personmember b where personMemberID = b.referenceid and a.personMemberID = 'JCNPRH39681' (A = B, B = '号码')

select a.personMemberID, * from chineseresume a,personmember b where a.personMemberID = b.referenceid and a.personMemberID = 'JCNPRH39681' and b.referenceid = 'JCNPRH39681' (A = B, B = '号码', A = '号码')

select a.personMemberID, * from chineseresume a,personmember b where b.referenceid = 'JCNPRH39681' and a.personMemberID = 'JCNPRH39681' (B = '号码', A = '号码')

26、尽量使用 EXISTS 代替 select count(1) 来判断是否存在记录。count 函数只有在统计表中所有行数时使用,而且 count(1) 比 count(*) 更有效率。

27、尽量使用 “>=”,不要使用 “>”。

28、索引的使用规范:

索引的创建要与应用结合考虑,建议大的 OLTP 表不要超过 6 个索引;
尽可能的使用索引字段作为查询条件,尤其是聚簇索引,必要时可以通过 index index_name 来强制指定索引;

避免对大表查询时进行 table scan,必要时考虑新建索引;

在使用索引字段作为条件时,如果该索引是联合索引,那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,否则该索引将不会被使用;

要注意索引的维护,周期性重建索引,重新编译存储过程。  

29、下列 SQL 条件语句中的列都建有恰当的索引,但执行速度却非常慢:

1

2

3

SELECT * FROM record WHERE substrINg(card_no, 1, 4) = '5378' --13秒

SELECT * FROM record WHERE amount/30 < 1000 --11秒

SELECT * FROM record WHERE convert(char(10), date, 112) = '19991201' --10秒

分析: WHERE 子句中对列的任何操作结果都是在 SQL 运行时逐列计算得到的,因此它不得不进行表搜索,而没有使用该列上面的索引。如果这些结果在查询编译时就能得到,那么就可以被 SQL 优化器优化,使用索引,避免表搜索,因此将 SQL 重写成下面这样:

1

2

3

SELECT * FROM record WHERE card_no like '5378%' -- < 1秒

SELECT * FROM record WHERE amount < 1000*30 -- < 1秒

SELECT * FROM record WHERE date = '1999/12/01' -- < 1秒

30、当有一批处理的插入或更新时,用批量插入或批量更新,绝不会一条条记录的去更新。

31、在所有的存储过程中,能够用 SQL 语句的,绝不用循环去实现。

32、选择最有效率的表名顺序(只在基于规则的优化器中有效):

Oracle 的解析器按照从右到左的顺序处理 FROM 子句中的表名,FROM 子句中写在最后的表(基础表 driving table)将被最先处理,在 FROM 子句中包含多个表的情况下,必须选择记录条数最少的表作为基础表。如果有 3 个以上的表连接查询,那就需要选择交叉表(intersection table)作为基础表,交叉表是指那个被其他表所引用的表。

33、提高 GROUP BY 语句的效率,可以通过将不需要的记录在 GROUP BY 之前过滤掉。

34、SQL 语句用大写,因为 Oracle 总是先解析 SQL 语句,把小写的字母转换成大写的再执行。

35、别名的使用,别名是大型数据库的应用技巧,就是表名、列名在查询中以一个字母为别名,查询速度要比建连接表快 1.5 倍。

36、避免死锁,在你的存储过程和触发器中访问同一个表时总以相同的顺序;事务应尽可能地缩短,减少数据量的涉及;永远不要在事务中等待用户输入。

37、避免使用临时表,除非有需要,可以使用表变量代替。大多数时候(99%),表变量驻扎在内存中,因此速度比临时表更快,临时表驻扎在 TempDb 数据库中,因此临时表上的操作需要跨数据库通信,速度自然慢。

38、最好不要使用触发器:

触发,执行一个触发器事件本身就是一个耗费资源的过程;
如果能够使用约束实现的,尽量不要使用触发器;

不要为不同的触发事件(Insert、Update 和 Delete)使用相同的触发器;

不要在触发器中使用事务型代码。

39、索引创建规则:

表的主键、外键必须有索引;
数据量超过 300 的表应该有索引;

经常与其他表进行连接的表,在连接字段上应该建立索引;

经常出现在 WHERE 子句中的字段,特别是大表的字段,应该建立索引;

索引应该建在选择性高的字段上;

索引应该建在小字段上,对于大的文本字段甚至超长字段,不要建索引;

复合索引的建立需要进行仔细分析,尽量考虑用单字段索引代替;

正确选择复合索引中的主列字段,一般是选择性较好的字段;

复合索引的几个字段是否经常同时以 AND 方式出现在 WHERE 子句中?单字段查询是否极少甚至没有?如果是,则可以建立复合索引;否则考虑单字段索引;

如果复合索引中包含的字段经常单独出现在 WHERE 子句中,则分解为多个单字段索引;

如果复合索引所包含的字段超过 3 个,那么仔细考虑其必要性,减少复合的字段;

如果既有单字段索引,又有这几个字段上的复合索引,一般可以删除复合索引;