何时使用EXCEPT而不是Transact SQL中的NOT EXISTS?

Joe*_*eda 38 sql t-sql sql-server performance sql-server-2005

我刚刚通过阅读同事编写的代码了解到SQL Server中存在新的"EXCEPT"子句(有点晚了,我知道......).这真让我惊讶!

但后来我对它的使用有一些疑问:什么时候建议使用它?使用它与使用"AND NOT EXISTS ..."的相关查询之间是否存在性能差异?

在阅读了BOL中的EXCEPT文章之后,我认为它只是第二个选项的简写,但是当我用它重写了几个查询时(因为他们对我更熟悉的"AND NOT EXISTS"语法)然后检查了,我感到很惊讶.执行计划 - 惊喜!EXCEPT版本的执行计划更短,执行速度也更快.总是这样吗?

所以我想知道:使用这个强大的工具的准则是什么?

Qua*_*noi 37

EXCEPTNULL值视为匹配.

这个查询:

WITH    q (value) AS
        (
        SELECT  NULL
        UNION ALL
        SELECT  1
        ),
        p (value) AS
        (
        SELECT  NULL
        UNION ALL
        SELECT  2
        )
SELECT  *
FROM    q
WHERE   value NOT IN
        (
        SELECT  value
        FROM    p
        )
Run Code Online (Sandbox Code Playgroud)

将返回一个空行集.

这个查询:

WITH    q (value) AS
        (
        SELECT  NULL
        UNION ALL
        SELECT  1
        ),
        p (value) AS
        (
        SELECT  NULL
        UNION ALL
        SELECT  2
        )
SELECT  *
FROM    q
WHERE   NOT EXISTS
        (
        SELECT  NULL
        FROM    p
        WHERE   p.value = q.value
        )
Run Code Online (Sandbox Code Playgroud)

将返回

NULL
1
Run Code Online (Sandbox Code Playgroud)

, 还有这个:

WITH    q (value) AS
        (
        SELECT  NULL
        UNION ALL
        SELECT  1
        ),
        p (value) AS
        (
        SELECT  NULL
        UNION ALL
        SELECT  2
        )
SELECT  *
FROM    q
EXCEPT
SELECT  *
FROM    p
Run Code Online (Sandbox Code Playgroud)

将返回:

1
Run Code Online (Sandbox Code Playgroud)

递归中的EXCEPT子句中也允许使用递归引用CTE,但它的行为方式很奇怪:它返回除前一组的最后一行之外的所有内容,而不是除了整个前一组之外的所有内容:

WITH    q (value) AS
        (
        SELECT  1
        UNION ALL
        SELECT  2
        UNION ALL
        SELECT  3
        ),
        rec (value) AS
        (
        SELECT  value
        FROM    q
        UNION ALL
        SELECT  *
        FROM    (
                SELECT  value
                FROM    q
                EXCEPT
                SELECT  value
                FROM    rec
                ) q2
        )
SELECT  TOP 10 *
FROM    rec

---
1
2
3
-- original set
1
2
-- everything except the last row of the previous set, that is 3
1
3
-- everything except the last row of the previous set, that is 2
1
2
-- everything except the last row of the previous set, that is 3, etc.
1
Run Code Online (Sandbox Code Playgroud)

SQL Server 开发人员必须忘记禁止它.


小智 8

我做了很多分析除了,不存在,不在和外连接.通常,左外连接是查找丢失行的最快,尤其是连接主键.如果您知道它将是select中返回的小列表,则In In可以非常快.

我使用EXCEPT来比较重写代码时返回的内容.运行旧代码保存结果.运行新代码保存结果,然后使用除捕获所有差异.这是一种快速简便的方法来查找差异,尤其是在需要获得包括null在内的所有差异时.非常适合即时轻松编码.

但是,每种情况都不同.我对我曾经指导过的每一位开发者说.试试吧.做各种各样的时间.试试吧,时间吧,做吧.


pri*_*kar 7

EXCEPT 比较两个全选的所有(成对)列。NOT EXISTS 根据 NOT EXISTS 关键字后的子查询中 WHERE 子句中指定的条件比较两个或多个表。

EXCEPT 可以使用 NOT EXISTS 重写。(EXCEPT ALL 可以使用 ROW_NUMBER 和 NOT EXISTS 重写。)

这里得到这个


Yis*_*hai 2

没有考虑 SQL Server 的执行计划。我总是发现,当出现性能问题时,当一种语法比另一种语法制定更好的执行计划时,这是完全任意的(从用户的角度来看,我确信算法编写者会理解为什么)。

在本例中,有关查询参数比较的某些操作允许 SQL 找出直接 select 语句无法找出的快捷方式。我确信这是算法的缺陷。换句话说,您可以在逻辑上插入相同的内容,但算法不会对存在的查询进行转换。有时这是因为能够可靠地计算出结果的算法比查询本身需要更长的执行时间,或者至少算法设计者是这么认为的。