代码改变世界

查询优化(5)——总结

2010-07-01 11:57  知行思新  阅读(1683)  评论(0编辑  收藏  举报

在前几篇中我们讨论了查询优化的几个方面。今天我们进行一个总结,把最重要的设计原则概括起来。

基于集合思考

对于SQL初学者,这是最需要注意的。我们应尽可能避免使用游标。

定义游标来扫描表中的行不符合数据库编程的思想。

  • 其比SQL Server内置的nested loop迭代运算低效,因为其对于每行数据需要更深的执行栈。
  • 查询优化器无法对其进行优化,如使用不同的连接顺序,执行顺序或使用不同的执行算法。
  • 其代码本身更复杂,更难于理解,维护和优化。

给优化器提供约束和统计信息

查询优化器生成的执行计划的性能和健壮性很大程度上依赖于可获得信息的质量。有两种信息我们可以提供给优化器:

约束:SQL Server的唯一性约束,外键约束和check约束都会影响执行计划。我们应声明数据所符合的尽可能多的约束。当优化器知道某些约束时,其能生成更为明确的执行计划。声明性约束比通过存储过程,函数或触发器实现的约束对优化器带来更多的好处。

统计信息:在可能的情况下,在所有数据库上打开AUTO_UPDATE_STATISTICS选项。如果在某些情况下我们必须手工维护统计信息,要记得在可能的时候重新开启AUTO_UPDATE_STATISTICS选项。统计信息缺失或不准确会使优化器作出错误的选择。

避免不必要的复杂性

对于有些查询构建方法,优化器无法总能给出最优执行计划。

  • 没有有效限制结果集的冗长查询。当没有进行有效的过滤造成可能的结果集足够大时,优化器只能“猜测”最优的执行计划。相对多重GROUP BY子句和子查询,优化器往往更善于处理JOIN操作。
  • 有些查询构建方法优化器无法很容易地估计符合条件的行数或数据分布。这些构建包括聚合,标量表达式,用户定义函数(UDF)和多语句表值函数(TVF)。

上述的查询构建会降低执行计划选择算法的可靠性,所以我们在使用上述构建时要小心。如下的查询构建会帮助优化器生成更可靠,更健壮的执行计划。

  • 为查询中需要用来过滤结果集的标量表达式创建计算列。
  • 在可能的情况下尽可能不使用复杂查询构建。在执行计划初期错误的估计基数或数据分布会影响其后的运算符选择。
  • 把复杂查询分成多步,把中间结果存储在临时表中。

小心动态SQL

SQL Server允许一些动态语言特性,其中的一些特性如下:

  • 未限定的对象名:在不同schema下的对象能有相同的名字。当一个查询使用一个未限定的对象,此对象会使用执行此查询用户的默认schema。由于没有限定对象的schema,在执行计划生成时需要附加的处理。所以使用对象时,推荐显示限定其schema。
  • 未限定的临时表:与未限定的对象名相同,最好显示限定其schema。
  • 动态生成的SQL语句:这是一个强大的功能,但会有安全性风险,例如SQL注入。所以尽可能少用动态SQL语句。
  • 改变连接设置:连接选项如ANSI_NULLS或ARITH_ABORT的改变,会影响查询的语义,也会影响生成的执行计划。改变连接选项会造成重编译,降低了查询的性能。最好对所以的应用,使用SQL Server的默认连接设置。

 

查询优化系列