SQL中如何筛选重叠日期范围?排查查询语句失效问题
如何筛选出日期范围重叠的行
先把你的示例数据整理成更清晰的表格,方便我们分析问题:
| sID | start_date | end_date | 行号 |
|---|---|---|---|
| 1 | 1995-07-28 | 2003-07-20 | 1 |
| 1 | 2003-07-21 | 2010-05-04 | 2 |
| 1 | 2010-05-03 | 2010-05-03 | 3 |
| 2 | 1960-01-01 | 2011-03-01 | 4 |
| 2 | 2011-03-02 | 2012-03-13 | 5 |
| 2 | 2012-03-12 | 2012-10-21 | 6 |
| 2 | 2012-10-22 | 2012-11-08 | 7 |
| 3 | 2003-07-23 | 2010-05-02 | 8 |
你的需求是找出同一sID下日期范围存在重叠的行,比如sID=1的第2、3行(第2行的日期范围完全包含第3行),以及sID=2的第5、6行(两者日期有交集)。
你之前的SQL没排除第1行的原因
大概率是你的筛选条件没精准区分「相邻日期」和「重叠日期」。比如sID=1的第1行end_date是2003-07-20,第2行start_date是2003-07-21,这属于相邻但不重叠,如果你的条件只写了a.end_date >= b.start_date这类单一限制,就会误把这种相邻情况当成重叠,从而把第1行也包含进来。
正确的SQL写法
要准确筛选重叠行,核心是明确重叠的判断逻辑:同一sID下的两行A和B,日期范围存在交集的条件是:A.start_date <= B.end_date AND A.end_date >= B.start_date。同时要避免匹配行自身和重复结果(比如A匹配B和B匹配A)。
这里提供两种实用的实现方式:
方式1:自连接+去重
SELECT DISTINCT t1.* FROM your_table t1 JOIN your_table t2 ON t1.sID = t2.sID AND t1.rowid != t2.rowid -- 用主键/rowid排除同一行,根据你的表结构调整 AND t1.start_date <= t2.end_date AND t1.end_date >= t2.start_date -- 如果只需要sID=1的重叠行,加上下面的WHERE条件 WHERE t1.sID = 1
方式2:窗口函数(适用于MySQL 8+/PostgreSQL/Oracle等)
用LEAD/LAG窗口函数直接对比当前行和相邻行的日期:
WITH ranked_data AS ( SELECT *, LEAD(start_date) OVER (PARTITION BY sID ORDER BY start_date) AS next_start, LAG(end_date) OVER (PARTITION BY sID ORDER BY start_date) AS prev_end FROM your_table ) SELECT sID, start_date, end_date FROM ranked_data WHERE -- 当前行与上一行重叠 (prev_end IS NOT NULL AND start_date <= prev_end) -- 或者当前行与下一行重叠 OR (next_start IS NOT NULL AND end_date >= next_start)
针对你的精准需求(仅返回sID=1的第2、3行)
如果只需要这两行,可以用EXISTS子查询精准过滤:
SELECT t1.* FROM your_table t1 WHERE t1.sID = 1 AND EXISTS ( SELECT 1 FROM your_table t2 WHERE t2.sID = t1.sID AND t2.rowid != t1.rowid AND t1.start_date <= t2.end_date AND t1.end_date >= t2.start_date )
这个查询会自动排除第1行(因为它和sID=1的其他行没有日期重叠),只返回存在重叠的第2、3行。
内容的提问来源于stack exchange,提问作者koala
相关产品推荐
相关产品推荐

