为什么IF(查询)需要一个多小时,查询时间不到一秒?

Gar*_*ill 11 sql sql-server

我有以下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)
Run Code Online (Sandbox Code Playgroud)

SomeTable表有大约200,000行,SomeOtherTable表格大致相同.

如果我执行内部SQL(SELECT),它会在亚秒内执行,不返回任何行.但是,如果我执行整个脚本(IF...RAISERROR),则需要一个多小时.为什么?

现在,显然,执行计划是不同的 - 我可以在企业管理器中看到 - 但是,为什么呢?

我可能会做SELECT @num = COUNT(*) WHERE......然后IF @num > 0 RAISERROR......但是......我认为这有点失误.如果您知道它存在,您只能编写一个错误代码(对我来说它确实看起来像个错误).


编辑:

我要指出,我已经尝试过重跳汰查询到OUTER JOIN按@波希米亚的答案,但本作的执行时间没有差异.


编辑2:

我附上了内部SELECT语句的查询计划:

查询计划 - 内部SELECT语句

...以及整个IF...RAISERROR块的查询计划:

查询计划 - 整个IF语句

显然这些显示了真实的表/字段名称,但除此之外,查询完全如上所示.

usr*_*usr 6

IF并不会神奇地关闭优化或破坏计划.优化器注意到EXISTS最多只需要一行(比如a TOP 1).这称为"行目标",通常在您进行分页时发生.但也有EXISTS,IN,NOT IN和这样的事情.

我的猜测:如果你写入TOP 1原始查询,你会得到相同(坏)的计划.

优化器尝试在这里变得聪明,并且只使用便宜得多的操作生成第一行.不幸的是,它错误地估计了基数.它猜测查询将产生大量行,但实际上它不产生任何行.如果它正确估计你只是得到一个更有效的计划,或者它根本不会进行转换.

我建议采取以下步骤:

  1. 通过查看索引和统计信息来修复计划
  2. 如果这没有帮助,请更改IF (SELECT COUNT(*) FROM ...) > 0将提供原始计划的查询,因为优化程序没有行目标.