在 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 (<cte contents>) AS src
,或将 CTE 中的代码放在视图中。以上是关于在 SQL 中检测到重叠,未在选择中使用子查询且未将重复记录添加到结果集中的主要内容,如果未能解决你的问题,请参考以下文章