【问题标题】:Detected overlapping in SQL, without using a subquery in the select and without adding duplicate records to the result set在 SQL 中检测到重叠,未在选择中使用子查询且未将重复记录添加到结果集中
【发布时间】:2021-09-23 14:15:47
【问题描述】:

我希望创建一个可以在 SQL 中检测到重叠的查询,而无需在选择中使用子查询,也不会向结果集中添加重复记录。

我知道可以这样做,但考虑到两个表中的记录数量,我正在寻找更快的解决方案。

SELECT x.T1Id
    ,CASE 
        WHEN MaxOtherColumn IS NULL
            THEN 0
        ELSE 1
        END HasOverlapInT2
    ,CASE 
        WHEN MaxOtherColumn IS NULL
            THEN OtherColumn
        ELSE MaxOtherColumn
        END MaxOtherColumn
FROM (
    SELECT CASE 
            WHEN EXISTS (
                    SELECT MAX(T2.OtherColumn)
                    FROM T2
                    WHERE T1.ValidFrom <= T2.ValidUntil
                        AND T2.ValidFrom <= T1.ValidUntil
                        AND t1.T1Id = T2.T1Id
                    )
                THEN 1
            ELSE NULL
            END MaxOtherColumn
        ,T1Id
        ,OtherColumn
    FROM T1
    ) x

我不想对每一行都执行存在,而是批量处理。

样本数据

表 1

T1Id (PK) ValidFrom ValidUntil OtherColumn
1 2021-01-01 2021-12-31 1
2 2021-09-23 2021-09-24 2

表 2

T2Id (PK) T1Id(FK) ValidFrom ValidUntil OtherColumn
1 1 2021-01-01 2021-05-31 9000
2 1 2021-06-01 2021-12-31 9001
3 2 2021-01-01 2021-01-05 7000

预期结果

T1Id HasOverlapInT2 MaxOtherColumn
1 1 9001
2 0 2

SQL 服务器兼容级别 = 130 (2016)

【问题讨论】:

  • 与大多数专业的 SQL DBMS 一样,SQL Server 拥有最先进的统计优化器,因此查询的好/坏性能不取决于子查询的存在/不存在。如果您的查询速度太慢,请发布查询计划和表定义,并说明您需要多快。
  • 您的查询没有产生预期的结果,所以在我们尝试优化性能之前让我们解决这个问题:dbfiddle.uk/…

标签: sql sql-server performance


【解决方案1】:

您可以使用CROSS APPLY 来实现您的预​​期结果(您当前的解决方案不会返回您的预期结果):

SELECT x.T1Id,
    CASE 
        WHEN MaxOtherColumn IS NULL
        THEN 0
        ELSE 1
    END HasOverlapInT2,
    CASE 
        WHEN MaxOtherColumn IS NULL
        THEN OtherColumn
        ELSE MaxOtherColumn
    END MaxOtherColumn
FROM
(
    SELECT t.MaxOtherColumn,
        T1Id,
        OtherColumn
    FROM T1
    CROSS APPLY
    (
        SELECT MAX(T2.OtherColumn) MaxOtherColumn
        FROM T2
        WHERE T1.ValidFrom <= T2.ValidUntil
        AND T2.ValidFrom <= T1.ValidUntil
        AND t1.T1Id = T2.T1Id
    ) t
) x

【讨论】:

    【解决方案2】:

    一种稍微不同的思考方式,以防APPLY 太令人费解:

    ;WITH src AS 
    (
      SELECT T1.T1Id, 
        T2Other = T2.OtherColumn, 
        T1Other = T1.OtherColumn,
        Overlap = CASE WHEN T2.ValidUntil >= T1.ValidFrom
                  AND T2.ValidFrom <= T1.ValidUntil THEN 1 
                  ELSE 0 END
      FROM dbo.T1
      INNER JOIN dbo.T2
        ON T1.T1Id = T2.T1Id
    )
    SELECT T1Id, 
      HasOverlapInT2 = MAX(Overlap), 
      MaxOtherColumn = MAX(CASE Overlap WHEN 1 THEN T2Other ELSE T1Other END)
    FROM src
    GROUP BY T1Id;
    

    关键是要考虑一种方法,通过一次通过两者来生成结果。虽然相关子查询并不总是意味着性能更差,但它可能会导致扫描一个表中的每一行。

    【讨论】:

    • Imo cte 更令人费解,但我想这只是因为我没有经常使用它们。
    • @diiN__________ 我认为递归 CTE 可能令人费解;这只是派生表的语法略有不同,以一种让您首先想到派生表的方式进行逻辑排序。没有 CTE 就很容易重写(例如,通过使用 FROM (&lt;cte contents&gt;) AS src,或将 CTE 中的代码放在视图中。
    猜你喜欢
    • 2021-12-29
    • 2019-06-13
    • 2020-01-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-12
    • 2019-03-26
    相关资源
    最近更新 更多