动态生成where子句的最佳方法是什么?

时间:2011-07-14 14:59:50

标签: sql performance sql-server-2008

我正在编写一个使用重型select语句的存储过程。存储过程接受大约15个参数作为过滤器,所有这些参数都是NULLable。

参数通常有两件事 - 检查x是否在高和低之间或检查列值是否在y中。

我主要担心的是我如何编写where子句。

示例:动态SQL非常慢,因此我不想编写where子句,然后将其传递给exec。

我不想做if High = null then High = max,因为那时我仍然会有一个处理能力并没有用处的声明。

我不想做(if High = null or X <= High),因为仍然会为每一行处理空检查,而且我听到传言会混淆索引。

简而言之,我正在寻求考虑性能的最佳实践指南。

3 个答案:

答案 0 :(得分:7)

动态SQL过去很慢,因为没有缓存动态生成的SQL的执行计划。这不再是这种情况,只要查询文本相同,动态SQL查询的执行计划就会被缓存。这意味着你应该:

  • 使用参数来避免参数值改变查询文本
  • 尝试对where子句进行排序,使它们以相同的顺序出现

只要你这样做,那么你的查询计划应该被缓存(每个可能的查询变体一个),动态SQL不会比任何普通查询慢。


您应避免使用其他建议(将各种参数设置为NULL)并且实际上可能执行得非常糟糕 - 语句只能有一个缓存查询计划,但最佳查询计划将取决于根据提供的值,参数可能会有很大差异。

例如,一组参数可能导致返回大部分表,在这种情况下,表扫描可能是最佳的。另一组参数可能导致返回单行,在这种情况下,行查找可能是最佳的。 SQL Server必须选择缓存这两个计划中的一个(可能是基于第一次运行查询时提供的参数的最佳计划),但无论选择哪种计划,查询都可能在相反的情况下执行得很糟糕。 (这是一种过度简化,但是我已经看到了这种情况的变化,并且会对性能产生非常显着的影响。)

最终这意味着:

  • 大多数情况下,选择的查询计划可能不是最佳的,或
  • 您可以强制SQL Server为每次执行生成新计划,这始终会产生合理的执行计划,但会消除计划缓存的优势。

您的替代方法的另一个缺点是它会导致更复杂的查询,这会使查询优化器难以正确优化查询。

由于这些原因,我认为动态SQL肯定是更好的选择。

答案 1 :(得分:3)

如果将动态SQL与占位符一起用于参数而不是将参数合并到语句中,并在打开游标时绑定参数,则可以缓存该语句并且不会很慢。

从下面评论:没有那么多(是的,可能是2 ^ 15但实际上更少,可能更像是2 ^ 4通常使用的)当前和缺失参数的组合;这些都可以缓存。如果实际参数值包含在WHERE子句中(我已经看过),则每个查询都是唯一的,不会被缓存。

答案 2 :(得分:1)

where子句表达式中处理无效性检查的一种方法是执行以下操作:

declare @myParameter int

select *
from dbo.foo t
where t.someColumn = coalesce( @myParameter , t.someColumn )

优化器仍然可以在t.someColumn上使用索引,并且您可以避免使用OR运算符(这通常是使用索引的过程。

这是一件值得关注的事情。

另一件事:我不得不在以前的工作中做同样的事情。天真编码的问题在于,由于以下原因中的任何一个原因,您可能会遇到性能不佳的问题:

  • 如果执行的第一个查询是使用您可能描述为“非标准”参数执行的,则缓存的执行计划可能会在更普通的情况下表现不佳。

  • 对于所有这些变量,优化器可以选择一个查询计划,该计划为大多数时间都不会使用的参数拨打。

  • 列表继续......

我最后做的是检测存储过程,记录它的调用方式。一旦我得到了一些基线数据,一点分析就向我展示了它使用的4种或5种最常见的方式。

这让我可以为这些最常见的方式添加场景,因此90%的呼叫者都获得了很好的性能,其余大多数都获得了良好的性能,并且曾经有一些特殊情况我们无法做任何事情(除非DBA愿意重新选择参与选择的一些表格......这似乎不太可能)。

此外,您应该将存储过程参数分配给存储过程中的局部变量。如果不这样做,则传递的参数会影响执行计划的缓存方式。通过这样做,参数的值成为表达式,不再影响执行计划的cachin。

此外,请注意存储过程重新编译。在繁忙的系统中,重新编译会对性能产生有害影响。如果存储过程在每次存储过程调用中被重新编译一次或多次,则重新编译会取出编译锁(A)阻止其他人执行存储过程,直到重新编译完成,并且(B)锁定涉及的各种资源/依赖项在重新编译中。在繁忙的系统中,您的DBA不太可能在阻止方面受到青睐。

这是Execution Plan Caching and Reuse

上的MSDN

希望这有帮助。