SQL 1: SELECT l_comment, o_comment FROM lineitem, order
WHERE l_oderkey >o_orderkey
SQL 2: SELECT l_comment, o_comment FROM lineitem, order
WHERE l_oderkey =o_orderkey
复制代码
在 SQL 1 中连接谓词是通过大于号连接的,DB2 只能采用嵌套循环连接 (Nested-Loop-Join) 这种最基本的方式来建立两个表之间的连接。在 SQL2 中连接谓词中采用“=”连接,此时 DB2 优化器选用了归并排序(MSJoin)的连接方式,它的 Total cost 比 SQL 1 的要低很多,具有较好的性能。注意这里 SQL 1与 SQL 2 在语义上是不等价的,在这里用这样的示例是为了说明连接谓词中不使用等号的写法会导致访问路径完全不同,从而影响查询性能。
SQL 1 的访问路径图
[attach]131839[/attach]
SQL 2 的访问路径图
[attach]131840[/attach] 4.6恰当安排 Group By 子句中列的顺序
通常情况下,SQL 语句中的 GROUP BY 子句会导致数据库不得不通过一个排序(SORT)操作来实现对数据的分组,而排序被认为是一个比较耗费 CPU 和内存的操作。实际上某些情况下,如果写法得当,当中的排序操作是可以避免的。具体来说,在写 GROUP BY 子句的时候,应该考虑到数据库中已经存在的索引的情况。如果 GROUP BY 子句中所有的列恰好包括在某个索引的键(Key column)的范围之内而且是处于开始的位置,那么在写 GROUP BY 子句的时候,就应该按照该索引上键的先后顺序来写 GROUP BY 子句。
比如说有如下的 SQL 语句:
SELECT C2, C3, C1, AVG(C4)
FROM T1
GROUP BY C2, C3, C1
复制代码
一般情况下,GROUP BY C2, C3, C1这样的写法都会导致数据库的一个排序操作。但假定表 T1 上已经存在一个索引 IX1(C1, C2, C3, C4), 这里注意到 GROUP BY 子句中引用到的列(C2,C3,C1)正好是索引 IX1 中的前三个键,那么就可以通过改变 GROUP BY 子句中列的顺序的办法来避免这个排序操作。
可以把 SQL 语句改写为如下所示:
SELECT C1, C2, C3, AVG(C4)
FROM T1
GROUP BY C1, C2, C3
复制代码
通过这样改变 GROUP BY 子句中列的顺序使其与索引 IX1 中的键顺序一致,数据库就可以利用 IX1 来访问其已经排序的键值并直接返回进行下一步操作,从而避免额外的排序操作,从而带来查询性能上的提高。
需要指出的是,通过这样改写 GROUP BY 子句来避免排序,可能会导致最终返回结果的顺序不一致。在实际的业务逻辑当中,需要用户来确认是否其关注返回结果的顺序性。
下面来看一个具体的示例:
SQL 1:SELECT AVG(o_shippriority) FROM order
GROUP BY o_custkey , o_orderkey, o_orderdate
SQL 2:SELECT AVG(o_shippriority) FROM order
GROUP BY o_orderkey, o_orderdate, o_custkey
复制代码
这里 2 个 SQL 唯一的差别就在于 GROUP BY 子句中列的顺序不同。假定order 表上存在一个索引 PXO@OKODCKSPOP (O_ORDERKEY,O_ORDERDATE,O_CUSTKEY,O_SHIPPRIORITY,O_ORDERPRIORITY)。
由于 SQL 1 中的 GROUP BY 子句的列顺序与索引 PXO@OKODCKSPOP 的键顺序不一致,DB2 无法直接利用这个索引,所以 DB2 需要基于这 3 个列做一次排序(Sort),然后进行分组合并,排序的结果还需要通过临时文件(Wkfile)来保存。如果调整 GROUP BY 子句中的列顺序如 SQL 2 所示,使其与索引 PXO@OKODCKSPOP 的键顺序一致,DB2 通过这个索引返回的结果就已经是有序的,这样就省去了排序操作。对比两者的访问路径图可以看出来,SQL 2 所花费的成本(Total Cost)会少很多,性能上有较大的提高。
SQL 1 的访问路径图
[attach]131841[/attach]
SQL 2 的访问路径图
[attach]131842[/attach] 4.7活用FETCH FIRST N ROWS ONLY字句
在 DB2 的 SQL 语法中,FETCH FIRST n ROWS ONLY 表示只取回结果集当中的前 n 条记录,在应用中经常会碰到判断满足条件的数据是否存在这样的逻辑,为达到目的有些程序员会用count(*)的结果来作为判断依据,众所周知count(*)的性能是不高的,对于此类场景可以考虑用FETCH FIRST 1 ROWS ONLY替代。 4.8多用indexable 谓词避免使用Non-indexable谓词
indexable的布尔类型谓词才能成为用来匹配索引的谓词,而Non-indexable的谓词是不能用来匹配索引的,在谓词设计中,首先需要应用的基本原则是尽量避免使用Non-indexable 谓词。
Non-indexable谓词包括:
1)谓词左边的列在表达式或函数中 DATE + 10 < CURRENTDATE, LENGTH(C1) = 4
2)与字段比较的对象是不同类型的值或变量,如变量定义与字段类型不符。
3)字段之间的比较 C1 < C2
4)BETWEEN col1 AND col2
5)非谓词,即包含NOT,NOT NULL,NOT LIKE,NOT IN,NOT BETWEEN,<>
6)LIKE谓词使用时将通配符放在最前面。C1 LIKE ‘%ABC’ 4.9谓次匹配尽量多的索引字段
一个查询语句谓词与索引的key column匹配越多,过滤率越高,查询的效率也越好。 4.10谓词中加入分区表分区键值
对于分区表而言,尽可能在SQL语句中加入分区键值列作为谓词,将查询范围限定在某一个或某几个分区中,避免整表扫描,可以极大地减少I/O开销。 5总结
本文主要介绍了当下使用最广泛的两类数据库(关系型数据库及非关系型数据库)的区别和各自优势进行了简要阐述。从农行核心应用实际出发,重点从开发人员角度,对关系型数据库查询语句的书写与数据库访问性能的关系进行了阐述,通过实例给出了高效SQL语句的书写方法及背后的技术原理。