在 SQL 中检测到重叠,未在选择中使用子查询且未将重复记录添加到结果集中

Posted

技术标签:

【中文标题】在 SQL 中检测到重叠,未在选择中使用子查询且未将重复记录添加到结果集中【英文标题】:Detected overlapping in SQL, without using a subquery in the select and without adding duplicate records to the result set 【发布时间】: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/… 【参考方案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;
示例db<>fiddle

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

【讨论】:

Imo cte 更令人费解,但我想这只是因为我没有经常使用它们。 @diiN__________ 我认为递归 CTE 可能令人费解;这只是派生表的语法略有不同,以一种让您首先想到派生表的方式进行逻辑排序。没有 CTE 就很容易重写(例如,通过使用 FROM (&lt;cte contents&gt;) AS src,或将 CTE 中的代码放在视图中。

以上是关于在 SQL 中检测到重叠,未在选择中使用子查询且未将重复记录添加到结果集中的主要内容,如果未能解决你的问题,请参考以下文章

检测重叠日期并更新最新记录SQL Server 2008

cURL 已启用但不工作且未在 phpinfo() 中显示

MySQL 未在子查询中使用 INDEX

如何证明在 SQL 中使用子选择查询会降低服务器的性能

多对多注释未在选择查询中生成连接

在 SQL 中检测和合并日期范围的连续重叠