【问题标题】:SQL Server performance issues when using EXCEPT in query在查询中使用 EXCEPT 时的 SQL Server 性能问题
【发布时间】:2019-07-02 19:46:11
【问题描述】:

一般来说,我有一个 SELECT 查询与三个独立的 SELECT 查询相结合。我在查询中使用 EXCEPTUNION 运算符。当独立执行查询时,我会在 1-2 秒内收到结果,但是当有 EXCEPT 运算符时,查询将需要几个小时。

查询结构(简化)如下:

SELECT DISTINCT FIELD_1, FIELD_2, FIELD_3 FROM MYTABLE

EXCEPT

(
SELECT DISTINCT FIELD_1, FIELD_2, FIELD_3 FROM MYTABLE WHERE XXX

UNION

SELECT DISTINCT FIELD_1, FIELD_2, FIELD_3 FROM MYTABLE WHERE YYY
)

是否有任何方法可以加快整个查询的速度,或者 EXCEPT 运算符一般来说太慢以至于应该避免使用?

【问题讨论】:

  • WHERE NOT EXISTS?
  • 表格有,而不是字段
  • 我将从hereherehere 开始。您还没有显示执行计划,这是需要的...但是由于您在所有三个查询中都使用 same 表,我敢肯定有一个更优雅的解决方案,例如 @987654326 @正如@jarlh 提供的那样。我看不出你为什么需要UNIONEXCEPT。此外,不需要DISTINCT
  • SELECT DISTINCT FIELD_1, FIELD_2, FIELD_3 FROM MYTABLE WHERE NOT XXX AND NOT YYY 怎么样?
  • 这三个查询真的都针对同一个表,还是只是你的混淆?

标签: sql sql-server union except


【解决方案1】:

您可以通过GROUP BY 做到这一点

SELECT FIELD_1, FIELD_2, FIELD_3 
FROM MYTABLE
GROUP BY FIELD_1, FIELD_2, FIELD_3 
HAVING MAX(CASE WHEN (XXX) OR (YYY) THEN 1 ELSE 0 END) = 0

【讨论】:

  • 好答案。重要提示:使用此(或此问题的大多数其他答案)的任何人都应确保将 XXX 括在自己的括号中,如果它代表复合条件。当然,YYY 也是如此。
  • 确实,好主意。可能这也将提供比我的简化(v3)更好的性能,取决于 XXX 或 YYY 的逻辑
  • @RichardII - 谢谢。为了安全起见,我还是添加了它们。
  • @RichardII。 . .如果过滤器采用复杂的逻辑(OR + AND),这将是可怕的。
  • @Yogesh,您的评论中的“这个”指的是什么?查询本身?我建议的改变?你认为这会是什么可怕的:性能?易读性?
【解决方案2】:

我会用NOT EXISTS 代替CTE

WITH CTE AS (
     <your union query>
)
SELECT mt.*
FROM MYTABLE mt
WHERE NOT EXISTS (SELECT 1 FROM CTE c WHERE c.FIELD_1 = mt.FIELD_1 AND . . . );   

【讨论】:

    【解决方案3】:

    首先我不会将字段用于除,最好使用 Id,因为它是可索引的

    下面的采石场会有更快的性能。

    这就是我的做法。

    SELECT DISTINCT FIELD_1, FIELD_2, FIELD_3 FROM MYTABLE
    where Id not in (
    SELECT Id FROM MYTABLE WHERE XXX and YYY
    )

    【讨论】:

    • OP 可能没有向表中添加新列所需的权限。这也没有必要。请参阅此答案的更新:stackoverflow.com/a/54594763/1633949 以获得更简单的方法。
    • 我知道我假设您在表中有一个主键。在表中有一个主键总是好的。
    【解决方案4】:

    有时通过将工作负载拆分为多个步骤来简化查询优化器的任务是有益的。特别是如果执行可能需要几个小时:

    -- Step 1
    SELECT FIELD_1, FIELD_2, FIELD_3 INTO #Step1 FROM
    (
    SELECT FIELD_1, FIELD_2, FIELD_3 FROM MYTABLE WHERE XXX    
    UNION  
    SELECT FIELD_1, FIELD_2, FIELD_3 FROM MYTABLE WHERE YYY
    ) d
    
    -- Step 2:
    SELECT DISTINCT FIELD_1, FIELD_2, FIELD_3 FROM MYTABLE    
    EXCEPT    
    SELECT FIELD_1, FIELD_2, FIELD_3  FROM #Step1
    

    请注意,删除了一些 DISTINCT 子句


    更新,版本 3。 基于 OP 的上次更新:

    Tab:是同一张表。第一个查询基本上给出了几乎 整个表和第二个 + 第三个查询是我需要的子集 带走第一个查询结果

    我相信整个查询可以重写为:

    SELECT DISTINCT FIELD_1, FIELD_2, FIELD_3 FROM MYTABLE ext
    WHERE NOT EXISTS (
    
    SELECT * FROM (
        SELECT FIELD_1, FIELD_2, FIELD_3 FROM MYTABLE  
        WHERE  ( (XXX) OR (YYY))  -- original filter
    )   list 
    WHERE
        list.FIELD_1 = ext.FIELD_1
    AND list.FIELD_2 = ext.FIELD_2
    AND list.FIELD_3 = ext.FIELD_3
    ) 
    

    【讨论】:

    • 您在步骤 1 中缺少FROM
    • 在这种情况下,使用 CTE 作为步骤 1
    • @benjaminmoskovits,CTE 不会将该部分隔离到自己的执行中。因此最终整个 SQL 命令将被编译成一个通用的查询计划。临时表的想法是拆分工作负载并使优化器更容易生成高效的查询计划。但是,OP 给出了一个重要的评论,即所有操作都涉及到一个表,所以我添加了 update 来回答
    • 我同意,如果是同一张表,就用WHERE NOT (XXX) AND NOT (YYY)
    • 简化无效。如果FIELD_1, FIELD_2, FIELD_3 组混合了匹配和不匹配xxx/yyy 条件的行,这将返回它们。如果存在符合这些条件的单行,则 OP 中的查询将排除一个组
    猜你喜欢
    • 1970-01-01
    • 2023-03-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多