我有以下SQL:
IF EXISTS
(
SELECT
1
FROM
SomeTable T1
WHERE
SomeField = 1
AND SomeOtherField = 1
AND NOT EXISTS(SELECT 1 FROM SomeOtherTable T2 WHERE T2.KeyField = T1.KeyField)
)
RAISERROR ('Blech.', 16, 1)
SomeTable
表有大约200,000行,SomeOtherTable
表大致相同。
如果我执行内部SQL(SELECT
),它会在亚秒内执行,不返回任何行。但是,如果我执行整个脚本(IF...RAISERROR
),则需要一个多小时。的为什么吗
现在,显然,执行计划是不同的 - 我可以在企业管理器中看到 - 但同样,为什么?
我可能会做SELECT @num = COUNT(*) WHERE
...然后IF @num > 0 RAISERROR
之类的事情,但......我认为这有点遗漏了这一点。如果您知道它存在,您只能编写一个错误代码(对我来说它确实看起来像个错误)。
修改:
我应该提一下,我已经尝试按照@ Bohemian的回答将查询重新连接到OUTER JOIN,但这对执行时间没有任何影响。
编辑2 :
我附上了内部SELECT
语句的查询计划:
...以及整个IF...RAISERROR
块的查询计划:
显然这些显示了真实的表/字段名称,但除此之外,查询完全如上所示。
答案 0 :(得分:6)
IF
不会神奇地关闭优化或破坏计划。优化器注意到EXISTS
最多只需要一行(如TOP 1
)。这称为“行目标”,通常在您进行分页时发生。但也有EXISTS
,IN
,NOT IN
等等。
我的猜测:如果您将TOP 1
写入原始查询,则会得到相同(错误)的计划。
优化器尝试在这里变得聪明,只使用便宜得多的操作生成第一行。不幸的是,它错误地估计了基数。它猜测查询将产生大量行,但实际上它不会产生任何行。如果它正确估计你只是得到一个更有效的计划,或者它根本不会进行转换。
我建议采取以下步骤:
IF (SELECT COUNT(*) FROM ...) > 0
,这将提供原始计划,因为优化程序没有行目标。答案 1 :(得分:2)
这可能是因为优化器可以弄清楚如何将查询转换为更有效的查询,但不知何故IF阻止了这一点。只有一个EXPLAIN会告诉你为什么查询花了这么长时间,但是我可以告诉你如何使整个事情更有效...而不是使用相关的子查询,这是非常低效的 - 你得到“n”子查询运行主表中的“n”行 - 使用JOIN。
试试这个:
IF EXISTS (
SELECT 1
FROM SomeTable T1
LEFT JOIN SomeOtherTable T2 ON T2.KeyField = T1.KeyField
WHERE SomeField = 1
AND SomeOtherField = 1
AND T2.KeyField IS NULL
) RAISERROR ('Blech.', 16, 1)
这里的“技巧”是使用s LEFT JOIN并通过在WHERE子句中测试null来过滤掉所有连接的行,该子句在连接之后执行。
答案 2 :(得分:0)
请尝试SELECT TOP 1 KeyField
。使用主键在我的猜测中会更快。
注意:我发布此答案是因为我无法发表评论。