通过添加未使用的WHERE条件来查询运行时间

Jon*_*ock 1 sql sql-server where-clause

我遇到了一个有趣的障碍(至少对我有趣).下面是我的查询的一般概念.假设@AuthorType是存储过程的输入,并且每个地方都有各种专门条件我已经发表评论.

SELECT *
FROM TBooks
WHERE
(--...SOME CONDITIONS)
OR
(@AuthorType = 1 AND --...DIFFERENT CONDITIONS)
OR
(@AuthorType = 2 AND --...STILL MORE CONDITIONS)
Run Code Online (Sandbox Code Playgroud)

对我来说有趣的是,如果我使用@AuthorType = 0执行此SP,它的运行速度比删除最后两组条件(为@AuthorType的特殊值添加条件的条件)要慢.

SQL Server不应该在运行时意识到永远不会满足这些条件并完全忽略它们吗?我所经历的差异并不小; 它大约是查询长度的两倍(1-2秒到3-5秒).

我希望SQL Server能够为我做太多优化吗?我是否真的需要为特殊条件设置3个独立的SP?

Rem*_*anu 6

SQL Server不应该在运行时意识到永远不会满足这些条件并完全忽略它们吗?

不,绝对不是.这里有两个因素在起作用.

  1. SQL Server不会保证布尔运算符短路.有关查询优化如何反转布尔表达式求值顺序的示例,请参阅On SQL Server布尔运算符短路.虽然第一印象看起来像是编程思维集的命令式C的错误,但对于声明性的面向SQL的世界来说,这是正确的做法.

  2. 或者是SQL SARGability的敌人.将SQL语句编译为执行计划,然后执行计划.该计划在调用之间重用(缓存).因此,SQL编译器必须生成一个适合所有单独OR情况的单个计划(@ AuthorType = 1 AND @ AuthorType = 2 AND @ AuthorType = 3).在生成查询计划时,就像@AuthorType在某种意义上一次拥有所有值一样.结果几乎总是最糟糕的计划,一个不能使任何索引受益的计划,因为各个OR分支相互矛盾,因此它最终扫描整个表并逐个检查行.

在你的情况下做的最好的事情,以及涉及布尔OR的任何其他情况,是在查询之外移动@AuthorType:

IF (@AuthorType = 1)
  SELECT ... FROM ... WHERE ...
ELSE IF (@AuthorType = 2)
  SELECT ... FROM ... WHERE ...
ELSE ...
Run Code Online (Sandbox Code Playgroud)

因为每个分支都清楚地分成了自己的语句,所以SQL可以为每个单独的案例创建适当的访问路径.

下一个最好的方法是使用UNION ALL,这是chadhoc已经建议的方式,并且是在视图或需要单个语句的其他地方(不允许IF)的正确方法.