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

时间:2009-11-02 18:39:37

标签: sql sql-server sql-server-2005 performance tsql

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

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

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

所以我想知道:使用这个强大工具有哪些指导原则?

5 个答案:

答案 0 :(得分:35)

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
        )

将返回一个空行集。

此查询:

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
        )

将返回

NULL
1

,这一个:

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

将返回:

1

在递归EXCEPT的{​​{1}}子句中也允许递归引用,尽管它的行为方式很奇怪:它返回除前一组最后一行之外的所有内容,不是除了整个前一集之外的所有东西:

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 开发人员必须忘记禁止它。

答案 1 :(得分:8)

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

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

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

答案 2 :(得分:3)

EXCEPT比较两个完整选择的所有(配对)列。 NOT EXISTS比较两个或多个表,这些表符合NOT EXISTS关键字后面的子查询中WHERE子句中指定的条件。

可以使用NOT EXISTS重写EXCEPT。 (除了ALL之外,可以使用ROW_NUMBER和NOT EXISTS重写。)

来自here

答案 3 :(得分:2)

没有考虑SQL服务器的执行计划。当一个语法产生更好的执行计划而不是另一个语法时,我总是发现当遇到性能问题时,它完全是任意的(从用户的角度来看,我确信算法编写者会理解为什么)。

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

答案 4 :(得分:2)

如果您的查询已经过微调,那么使用EXCEPT子句和NOT EXIST / NOT IN没有性能差异。第一次在我将相关查询更改为其后运行EXCEPT时...我很惊讶因为它仅在7秒内返回结果,而相关查询在22秒内返回..然后我在相关查询中使用了distinct子句并重新启动..它也在7秒内返回..所以当你不知道或者不同时,EXCEPT很好没有时间对你的查询进行微调,否则两者的表现都相同......