上面提到代码清单3-1中的代码存在一些性能问题,接下来分析这条语句究竟在什么地方存在性能问题。
1.未实现只查询出需要的字段
如3.2.1节提到的,在编写查询语句时,应避免使用SELECT *这样的语句。只查询出需要的字段,可以避免许多不必要的I/O。在代码清单3-2中,将“*”替换成了查询时所需要的三个字段,这样就会避免不必要的数据在网络中传输,更重要的是,减少了字段的输出,可更有效地利用覆盖索引。在本示例中,将避免检索出Person.StateProvince表中的数据,其具体原因将在后续讲解执行计划时进行详细介绍。
代码清单3-2 查找Colorado州信息
SELECT a.City,a.PostalCode,a.AddressLine1 FROM Person.Address AS a JOIN Person.StateProvince AS sp ON a.StateProvinceID = sp.StateProvinceID WHERE sp.Name='Colorado'
2.未能限定好查询出的结果集
编写查询语句时,需要注意最终可能返回的结果集的大小。当结果集较大时,就应考虑是否可以限制结果集大小(例如,使用TOP子句)或使用分布算法对数据进行分页处理。
限定结果集可以避免大批量数据的操作,并且有效地使用索引,防止扫描操作带来大量的I/O和内存的使用。
3.未能很好地使用有效的索引
当需要对数据进行过滤的时候,应该优先考虑使用索引字段;若有多个索引字段可供选择,优先选择以筛选率较高,也就是重复率比较低的索引字段作为过滤条件。一般会选择将数据重复率低于5%的字段作为索引过滤字段。在代码清单3-1中,Person.StateProvince表中的Name字段是有索引的。数据库引擎将分析这一信息,优先使用Name字段上的索引进行数据定位,而后再根据关联字段StateProvinceId查询Person.Address表中的数据。分析这一执行过程产生的流程在数据库引擎中,被称为执行计划。关于执行计划的详细介绍,请参考第6章。
在编写查询语句时,有以下几个容易被忽略的要点。
1)不要在过滤字段上使用任何的计算,包括函数、逻辑计算、普通的计算等,因为这些计算公式的加入,将造成查询优化器无法使用相应字段的索引。
2)尽量使用有索引的字段进行排序,特别是排序的数据量比较大时,这可以很大程度上降低排序操作带来的成本开销。
3)填写查询表时,尽量使用JOIN关键字连接表,这样的语句才清晰,易于阅读,不易缺失关联条件。关联条件的缺失容易造成查询迪卡儿积。笔者在平常的调优中,时常会发现这样的情况,原本只需要查询百条数据,其查询结果却变成了数十万条,计算的数据量积呈几何倍增加了。