假设我有一个类似下面的 sql 语句,变量 @FOO 在代码的前面某处设置:
SELECT FIELDLIST
FROM TABLE
WHERE
(FIELD = @FOO OR @FOO IS NULL)
查询优化器是否足够聪明,可以先执行 OR 的第二面(@FOO IS NULL),因为(另一个假设)执行空值检查比执行字段比较更快?
我做了一些非正式的基准测试,不仅没有看到差异,而且我的尝试得到了不同的结果时间,这使我无法进行适当的比较。
假设我有一个类似下面的 sql 语句,变量 @FOO 在代码的前面某处设置:
SELECT FIELDLIST
FROM TABLE
WHERE
(FIELD = @FOO OR @FOO IS NULL)
查询优化器是否足够聪明,可以先执行 OR 的第二面(@FOO IS NULL),因为(另一个假设)执行空值检查比执行字段比较更快?
我做了一些非正式的基准测试,不仅没有看到差异,而且我的尝试得到了不同的结果时间,这使我无法进行适当的比较。
简短的回答...
是的,优化器足够聪明。
更长的答案...
SQL 是声明性的而不是命令性的:您的查询是对结果必须满足的条件的描述,而不是关于如何生成这些结果的分步说明。
优化器以最有效的顺序执行查询。它不能保证以任何特定的顺序评估您的条款,甚至根本评估它们 - 如果它可以在不评估特定条款的情况下获得正确的结果,那么它为什么要打扰呢?
任何特定查询的实际评估顺序是一个实现细节,并且可以随时间变化(例如,随着表的统计信息的变化)。
在实践中,优化器偶尔会出错,但在这种特殊情况下 - 将变量与 NULL 与从表或索引中读取进行比较 - 我认为它不太可能搞砸,尽管您可能想考虑使用OPTION(RECOMPILE)
或OPTION(OPTIMIZE FOR ...)
。
尝试使用相反顺序的子句对其进行测试:
SELECT FIELDLIST
FROM TABLE
WHERE
(@FOO IS NULL OR FIELD = @FOO)
您可能会发现第一个测试会使第二个测试短路,但反之亦然。
根据我的经验,有时使用两个查询和一个“UNION”而不是“OR”子句会更快。
SELECT FIELDLIST
FROM TABLE
WHERE
(FIELD = @FOO)
UNION
SELECT FIELDLIST
FROM TABLE
WHERE
(@FOO IS NULL)
这种方法有重复 SELECT 语句的缺点,但是 1500% 的性能提升证明了它的合理性。当然,这取决于数据库结构(在我的情况下它非常糟糕,我无法更改它)。
首先应用更快的条件是足够聪明的,假设它可以判断在一般情况下哪个比较会更快。在这种情况下,NULL 检查几乎总是更快,因为它必须从表达式的每一侧最多比较一个字节,并且可以将其分解。
如果此查询位于存储过程中,则此处可能起作用的一个因素是“参数嗅探”。这可能导致不一致的查询响应时间。要解决此问题,请在 sproc 中声明一个内部变量并将此变量分配给参数值,然后在 where 子句中使用内部变量或在 sproc 中使用RECOMPILE子句。关于这个主题有很多链接。