我遇到了一个有趣的障碍(至少对我来说很有趣)。以下是查询的大致概念。假设@AuthorType是存储过程的输入,并且我在每个地方添加了注释,都有各种特殊条件。
SELECT * FROM TBooks WHERE (--...SOME CONDITIONS) OR (@AuthorType = 1 AND --...DIFFERENT CONDITIONS) OR (@AuthorType = 2 AND --...STILL MORE CONDITIONS)
对我来说有趣的是,如果我使用@AuthorType = 0执行此SP,则它的运行速度比如果删除最后两组条件(为@AuthorType的特殊值添加条件的条件)要慢。
SQL Server难道不应该在运行时意识到永远不会满足这些条件并完全忽略它们吗?我所经历的差异并不小;大约是查询时间的两倍(1-2秒到3-5秒)。
我是否期望SQL Server对我进行太多优化?我是否真的需要3个单独的SP用于特殊条件?
SQL Server难道不应该在运行时意识到永远不会满足这些条件并完全忽略它们吗?
不,绝对不是。这里有两个因素在起作用。
SQL Server不会 不 保证布尔运算符短路。有关示例,请参阅“在SQL Server上布尔运算符短路”中清楚地显示了查询优化如何颠倒布尔表达式求值的顺序。乍一看,这似乎是命令式C语言编程心态的一个错误,但对于面向声明性集的SQL世界而言,这是正确的做法。
OR是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 ...
因为每个分支都清楚地分成了自己的语句,所以SQL可以为每种情况创建正确的访问路径。
第二个最好的方法是使用chadhoc已经建议的UNION ALL,并且在需要单个语句(不允许IF)的视图或其他地方使用正确的方法。