SQL 语句性能调优 初级篇 —— 简单查询语句的调优
2010-02-09 09:32
417 查看
SQL 语句性能调优
初级篇 —— 简单查询语句的调优李明慧, 软件工程师, IBM
简介: 经常听到有做应用的朋友抱怨数据库的性能问题,比如非常低的并发,令人崩溃的响应时间,长时间的锁等待,锁升级,甚至是死锁,等等。本文针对应用开发人员经常接触的 SQL 书写部分进行优化,以期望能对数据库开发人员有所帮助。
发布日期: 2010 年 2 月 04 日
级别: 初级
平均分 (共 15 个评分 )
经常听到有做应用的朋友抱怨数据库的性能问题,比如非常低的并发,令人崩溃的响应时间,长时间的锁等待,锁升级 , 甚至是死锁,等等。在解决这些问题的过程中,DBA 经常发现应用开发人员对数据库的“误用”。包括 , 返回过多不必要的数据 , 不必要和不适当加锁,对隔离级别的误用和对存储过程的误用等等。但是,面对浩如烟海的数据库知识 , 要求完全掌握 , 对应用开发人员来说也确实枯燥艰深 . 因此,笔者特别提炼对应用开发人员有帮助的 SQL 书写部分,以期望能对数据库开发人员有所帮助。
“根据我们的经验(由很多业界专家证明),在 SQL Server 上取得的性能提高有 80% 来自对 SQL 编码的改进,而不是来自于对于配置或系统性能的调整。”
—凯文 克莱恩等,Transact-SQL Programming 作者
“经验表明 80%-90% 的性能调优是在应用级做的,而不是在数据库级”
—托马斯 白特,Expert One on One: Oracle 作者
本文将主要讨论基于语法的优化以及简简单的查询条件。基于语法的优化指的是为不考虑任何的非语法因素(例如,索引,表大小和存储等),仅考虑在 SQL 语句中对于词语的选择以及书写的顺序。
一般规则
这一部分,将看一下一些在书写简单查询语时需要注意的通用的规则。
根据权值来优化查询条件
最好的查询语句是将简单的比较操作作用于最少的行上。以下两张表,表 1 和表 2 以由好到差的顺序列出了典型查询条件操作符并赋与权值。
表 1. 查询条件中操作符的权值
操作符 | 权值 |
= | 10 |
> | 5 |
>= | 5 |
< | 5 |
<= | 5 |
LIKE | 3 |
<> | 0 |
操作数 | 权值 |
仅常量字符 | 10 |
仅有列名 | 5 |
仅有参数 | 5 |
多操作数表达式 | 3 |
精确数值类型 | 2 |
其它数值类型 | 1 |
时间数据类型 | 1 |
字符数据类型 | 0 |
NULL | 0 |
… WHERE smallint_column = 789 |
左侧只有列名(smallint_column)得 5 分
操作数为精确数据类型(smallint_column)得 2 分
等号(=)操作符得 10 分
右侧是文字字符(789)得 10 分
下面是另外一个例子
… WHERE char_column >= varchar_column || ‘ x ’ |
左侧只有列名(char_column)得 5 分
CHAR 类型的操作数得 0 分
大于等于操作符得 5 分
左侧是多操作数表达示得 3 分
VARCHAR 类型的操作数得 0 分
上面表格中的权值数可能在不同类型的数据库系统中会有所不同,所以记住这些具体数值是没有意义的,只需要了解它们的排序即可。用时越少的比较条件,得分也就越高,这样的比较条件通常是那些操作的行数少或者易于比较的。
传递法则
传递法则是这样定义的:
IF (A <comparison operator> B) IS TRUE AND (B <comparison operator> C) IS TRUE THEN (A <comparison operator> C) IS TRUE AND NOT (A <comparison operator> C) IS FALSE |
通过传递法则,我们可以看出,可以用 C 来替换 B,而不使表达式的意思发生变化。
下面的两个例子表达了同样的含义,但是第二个表达示要比第一个表达式执行的快。
表达式一:
... WHERE column1 < column2 AND column2 = column3 AND column1 = 5 |
... WHERE 5 < column2 AND column2 = column3 AND column1 = 5 |
SELECT * FROM Table1 WHERE column1 = 5 AND NOT (column3 = 7 OR column1 = column2) |
SELECT * FROM Table1 WHERE column1 = 5 AND column3 <> 7 AND column2 <> 5 |
1. Sargability
理想的 SQL 表达式应该采用下面这种通用的格式:
<column> <comparison operator> <literal> |
根据这一规则,查询条件的左侧应该是一个列名;右侧应该是一个很容易进行查找的值。
遵循这一规则,所有的数据库系统都会将如下的表达式:
5 = column1 |
column1 = 5 |
例如:
... WHERE column1 - 3 = -column2 |
... WHERE column1 = -column2 + 3 |
回页首
针对专门操作符的调优
前面,讲的是关于查询条件的一般规则,在这一节中,将讨论如何使用专门的操作符来改进 SQL 代码的性能。
与 (AND)
数据库系统按着从左到右的顺序来解析一个系列由 AND 连接的表达式,但是 Oracle 却是个例外,它是从右向左地解析表达式。可以利用数据库系统的这一特性,来将概率小的表达示放在前面,或者是如果两个表达式可能性相同,那么可将相对不复杂的表达式放在前面。这样做的话,如果第一个表达式为假的话,那么数据库系统就不必再费力去解析第二个表达式了。例如,可以这样转换:
... WHERE column1 = 'A' AND column2 = 'B' |
... WHERE column2 = 'B' AND column1 = 'A' |
或 (OR)
和与 (AND) 操作符相反,在用或 (OR) 操作符写 SQL 语句时,就应该将概率大的表达示放在左面,因为如果第一个表达示为假的话,OR 操作符意味着需要进行下一个表达示的解析。
与 + 或
按照集合的展开法则,
A AND (B OR C) 与 (A AND B) OR (A AND C) 是等价表达示。 |
SELECT * FROM Table1 WHERE (column1 = 1 AND column2 = 'A') OR (column1 = 1 AND column2 = 'B') |
Row# | Colmun1 | Column2 |
1 | 3 | A |
2 | 2 | B |
3 | 1 | C |
索引查找 column1 = 1, 结果集 = {row 3}
索引查找 column2 = ‘ A ’ , 结果集 = {row1}
AND 合并结果集,结果集 = {}
索引查找 column 1 = 1, 结果集 = {row 3}
索引查找 column 2 = ‘ B ’ , 结果集 = {row2}
AND 合并结果集,结果集 = {}
OR 合并结集,结果集 = {}
现在根据集合的展开法则,对上面的语句进行转换:
SELECT * FROM Table1 WHERE column1 = 1 AND (column2 = 'A' OR column2 = 'B') |
索引查找 column2 = ‘ A ’ , 结果集 = {row1}
索引查找 column 2 = ‘ B ’ , 结果集 = {row2}
OR 合并结集,结果集 = {}
索引查找 column1 = 1, 结果集 = {row 3}
AND 合并结果集,结果集 = {}
由此可见搜索次数少了一次。虽然一些数据库操作系统会自动的进行这样的转换,但是对于简单的查询来说,这样的转换还是有好处的。
非 (NOT)
让非 (NOT) 表达示转换成更易读的形式。简单的条件能通过将比较操作符进行反转来达到转换的目的,例如:
... WHERE NOT (column1 > 5) |
... WHERE column1 <= 5 |
NOT (A AND B) = (NOT A) OR (NOT B) 和 NOT (A OR B) = (NOT A) AND (NOT B) |
... WHERE NOT (column1 > 5 OR column2 = 7) |
... WHERE column1 <= 5 |
... WHERE NOT (column1 = 0) |
... WHERE column <0 OR column > 0 |
很多人认为如下的两个查询条件没有什么差别,因为它们返回的结果集是相同的:
条件 1:
... WHEREcolumn1 = 5 |
... WHERE column1 IN (5, 6) |
当 IN 操作符,是一系列密集的整型数字时,最好是查找哪些值不符合条件,而不是查找哪些值符合条件,因此,如下的查询条件就应该进行如下的转换:
... WHERE column1 IN (1, 3, 4, 5) |
... WHERE column1 BETWEEN 1 AND 5 AND column1 <> 2 |
UNION
在 SQL 中,两个表的 UNION 就是两个表中不重复的值的集合,即 UNION 操作符返返回的两个或多个查询结果中不重复行的集合。这是一个很好的合并数据的方法,但是这并不是最好的方法。
查询 1:
SELECT * FROM Table1 |
SELECT DISTINCT * FROM Table1 |
第一个优化缺陷就是很多优化器只优化一个 SELECT 语句中一个 WHERE 语句,所以查询 1 的两个 SELECT 语句都被执行。首先优化器根据查询条件 column1 = 5 为真来查找所有符合条件的所有行,然后据查询条件 column2 = 5 为真来查找所有符合条件的所有行,即两次表扫描,因此,如果 column1 = 5 没有索引的话,查询 1 将需要 2 倍于查询 2 所需的时间。如果 column1 = 5 有索引的话,仍然需要二次扫描,但是只有在某些数据库系统存在一个不常见的优化缺陷却将第一个优化缺陷给弥补了。当一些优化器发现查询中存在 OR 操作符时,就不使用索引查询,所以在这种情况下,并且只有在这种情况下,UNION 才比 OR 性能更高。这种情况很少见,所以仍然建议大家当待查询的列没有索引时使用 OR 来代替 UNION。
回页首
总结
以上是作者对如何提高 SQL 性能的一些总结,这些规则并一定在所有的数据库系统上都能带来性能的提高,但是它们一定不会对数据库的性能带来下降,所以掌握并使用这些规则可以对数据库应用程序的开发有所帮助。本文总结的是一些 SQL 性能调优的比较初级的方面,SQL 调优还包括 Order by,Group by 以及 Index 等等,作者将来后续的文章中进行讲解。
相关文章推荐
- 【转】SQL点滴10—使用with语句来写一个稍微复杂sql语句,附加和子查询的性能对比
- (走向DBA[MSSQL篇] - 从SQL语句的角度提高数据库的访问性能)一些SQL查询语句应加上nolock
- Sql中的SET语句及调优查询的主要方法
- sql server 查询性能最差的sql语句
- Oracle 性能测试一:嵌套SQL的查询速度比较分析(初级)
- sql server性能分析--查询死锁的sql语句
- Oracle Sql 语句性能调优
- SQL 语句性能调优
- 简单查询和联合查询两方面介绍SQL查询语句
- 使用with语句来写一个稍微复杂sql语句,附加和子查询的性能对比
- .Net+SQL Server企业应用性能优化笔记3——SQL查询语句
- oracle学习---简单的sql语句查询
- .Net+SQL Server企业应用性能优化笔记3——SQL查询语句
- 巧用sql语句以提高数据库查询性能
- SQL点滴10—使用with语句来写一个稍微复杂sql语句,附加和子查询的性能对比
- 根据会员编号、会员名称、会员积分、店铺编号查询数据的简单方法(sql语句)
- 【MySql性能优化二】利用explain进行查询和分析sql语句
- SQL语句的性能查询
- SQL查询语句简要使用精华——之 简单查询
- Oracle性能-查询统计信息的SQL语句