2011-07-14 158 views
3

我正在编写一个存储过程,可以处理大量的select语句。存储过程需要约15个参数才能充当过滤器,所有这些参数都是可以空的。什么是动态生成where子句的最佳方式?

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

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

示例:动态SQL非常慢,所以我不想写入where子句,然后将它传递给exec。

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

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

总之,我正在寻找将性能考虑在内的最佳实践的指导。

+3

看一看http://www.sommarskog.se/dyn-search.html –

+0

是什么让你说动态SQL的速度非常慢?另外,在我根据他们做出设计决策之前,我会验证任何谣言。 –

+0

@Alex K:你会相信几秒钟之前有人刚刚注意到我完全一样的网址吗?谢谢:) – JSWork

回答

7

由于动态生成的SQL的执行计划没有被缓存,动态SQL过去很慢。现在不再这样了,只要查询文本是相同的,动态SQL查询的执行计划就会被缓存。这就是说,你应该:

  • 使用的参数,以避免您的参数值改变查询文本
  • 试图解决您的where子句,使他们

只要出现在相同的顺序,你做到这一点,那么你的查询计划应该被缓存(每个可能的查询变体一个)并且动态SQL不会比任何普通查询慢。


你的其他建议(设置各种参数,以NULL)是要避免的,而且实际上可能相当严重执行 - 语句只可能有一个缓存的查询计划,但是最佳查询计划将取决于参数根据提供的价值可能会有很大的不同。

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

最终,这将意味着,要么:

  • 大部分的查询计划选择可能不会是最优您可以强制SQL Server生成总是导致每次执行新的计划时,或
  • 在一个合理的执行计划,但消除了计划缓存的优势

你选择的另一个缺点aproaches是,它会导致更复杂的查询,它可以使人们难以对查询优化器来正确优化查询。

由于这些原因,我会说动态SQL绝对是更好的选择。对于检查处理为无效的where条款表达

3

如果使用带参数的占位符的动态SQL而不是将参数合并到语句中,并在打开游标时绑定参数,则可以缓存该语句并且速度不会太慢。

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

+0

AFAIK - 由于OP建议根据不同的搜索参数使用(或不使用)建立不同的WHERE子句,因此WHERE子句中的更改将导致缺少缓存执行计划。 – MatBailie

+0

@Dems - 但不会有那么多(是的,可能2^15,但在实践中少得多,可能更常用2^4)现有和缺失参数的组合;这些可以被缓存。如果实际参数值包含在WHERE子句中(我已经看过),每个查询都是唯一的,不会被缓存。 – antlersoft

+0

添加到您的答案,我现在同意:) – MatBailie

1

一种方法是做这样的事情:

declare @myParameter int 

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

优化仍然可以在t.someColumn使用索引和你避免OR操作(这是什么通常bolluxes了使用索引的

这是一件事看

另一件事:我不得不做同样的事情在以前的工作中proble。米,编码它天真的是,你可能得到性能差的多种原因中的任何一个:如果执行的第一个查询与你可能会形容为“非标”执行

  • 参数,对于更普通的情况,缓存的执行计划很可能表现不佳。

  • 对于所有这些变量,优化程序可能会选择一个查询计划,该计划的参数可能在大多数情况下甚至不会使用。

  • 不胜枚举...

我伤口什么事做,是仪器的存储过程,记录它是如何被调用。一旦我有了一些基准数据,一点分析就会告诉我它使用的4种或5种最常用的方法。

这使我可以为这些最常见的方法添加场景,所以90%的调用者获得了很好的性能,其余大部分都获得了良好的性能,并且曾经有过特殊情况我们无法做任何事情(除非数据库管理员愿意重新选择参与选择的一些表......这看起来不太可能)。

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

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

这里有Execution Plan Caching and Reuse

希望这有助于MSDN。

相关问题