【问题标题】:SQL Server find overlaping date rangesSQL Server 查找重叠的日期范围
【发布时间】:2024-08-19 03:45:02
【问题描述】:

我有一个表格,其中包含每年的日期范围(季节)。 正常的是,一个赛季的结束是下一赛季的开始。 在下面的示例中,我用粗体显示了两个不规则的季节设置。第一季的结束是在第二季开始的第二天。第二季的开始是在第三季结束的第二天

+--------+----+--------------+--------------+
|SEASONID|YEAR|DATE FROM     |DATE TO       |
+--------+----+--------------+--------------+
|1       |14  |  2014-01-01  |**2014-01-31**|
|2       |14  |**2014-01-30**|  2014-03-01  |
|3       |14  |  2014-03-01  |**2014-05-22**|
|4       |14  |**2014-05-23**|  2014-10-16  |
|5       |14  |  2014-10-16  |  2014-12-01  |
+--------+----+--------------+--------------+

有没有办法编写一个可以捕获未正确设置的季节的查询? (一个赛季的结束不是下一个赛季的开始)

【问题讨论】:

  • id是否自增?
  • 这只是另一个孤岛和差距问题。尝试谷歌搜索或搜索,您会找到问题描述和大量答案。
  • 不幸的是,SeasonID 是自动递增的,并且没有规定每年都会按顺序插入其季节。这使得所有依赖于 id = id + 1 的查询都没用:(

标签: sql sql-server date datetime gaps-and-islands


【解决方案1】:

希望对你有帮助

select * from stest st
join stest st1
on st.edate ! = st1.sdate 
where st1.id = st.id + 1

其中 stest 具有以下详细信息

+--+----+----------+----------+
|id|year|sdate     |edate     |
+--+----+----------+----------+
|1 |14  |2014-01-01|2014-01-31|
+--+----+----------+----------+
|2 |14  |2014-01-30|2014-03-01|
+--+----+----------+----------+
|3 |14  |2014-03-01|2014-05-22|
+--+----+----------+----------+
|4 |14  |2014-05-23|2014-10-16|
+--+----+----------+----------+
|5 |14  |2014-10-16|2014-12-01|
+--+----+----------+----------+

上面的查询会给出如下结果

+--+----+----------+----------+--+----+----------+----------+
|id|year|sdate     |edate     |id|year|sdate     |edate     |
+--+----+----------+----------+--+----+----------+----------+
|1 |14  |2014-01-01|2014-01-31|2 |14  |2014-01-30|2014-03-01|
+--+----+----------+----------+--+----+----------+----------+
|3 |14  |2014-03-01|2014-05-22|4 |14  |2014-05-23|2014-10-16|
+--+----+----------+----------+--+----+----------+----------+

从结果可以得到 id (1,2) 和 (3,4) 不匹配

如果你有类似下面的操作

select * from stest st
join stest st1
on st.edate  = st1.sdate 
where st1.id = st.id + 1

然后你会得到匹配结果

+--+----+----------+----------+--+----+----------+----------+
|id|year|sdate     |edateid   |id|year|sdate     |edate     |
+--+----+----------+----------+--+----+----------+----------+
|2 |14  |2014-01-30|2014-03-01|3 |14  |2014-03-01|2014-05-22|
+--+----+----------+----------+--+----+----------+----------+
|4 |14  |2014-05-23|2014-10-16|5 |14  |2014-10-16|2014-12-01|
+--+----+----------+----------+--+----+----------+----------+

【讨论】:

  • 非常感谢,但 Uuthat 每年都会按顺序插入其季节。这使得所有依赖于 id = id + 1 的查询都没用:(
【解决方案2】:

这回答了你一半的问题:使用来自this article 的重叠日期查询来查找冲突记录:

-- 1.2) select date ranges that overlap [d1, d2) (d2 and end_date are exclusive)
-- SELECT * FROM <table> WHERE @d2 > start_date AND end_date > @d1

SELECT s1.*
FROM seasons AS s1
INNER JOIN seasons AS s2 ON s1.seasonid <> s2.seasonid
AND s2.date_to > s1.date_from
AND s1.date_to > s2.date_from

结果:

+--------+----+----------+----------+--------+----+----------+----------+
|seasonid|year|date_from |date_to   |seasonid|year|date_from |date_to   |
+--------+----+----------+----------+--------+----+----------+----------+
|1       |14  |2014-01-01|2014-01-31|2       |14  |2014-01-30|2014-03-01|
+--------+----+----------+----------+--------+----+----------+----------+
|2       |14  |2014-01-30|2014-03-01|1       |14  |2014-01-01|2014-01-31|
+--------+----+----------+----------+--------+----+----------+----------+

SQL Fiddle

【讨论】:

    【解决方案3】:
    DECLARE @T TABLE (
        SeasonId    INT     NOT NULL,
        Year        INT     NOT NULL,
        DateFrom    DATE    NOT NULL,
        DateTo      DATE    NOT NULL
    )
    
    INSERT @T VALUES (1, 14, '2014-01-01', '2014-01-31')
    INSERT @T VALUES (2, 14, '2014-01-30', '2014-03-01')
    INSERT @T VALUES (3, 14, '2014-03-01', '2014-05-22')
    INSERT @T VALUES (4, 14, '2014-05-23', '2014-10-16')
    INSERT @T VALUES (5, 14, '2014-10-16', '2014-12-01')
    
    ;WITH T AS (
        SELECT *, ROW_NUMBER() OVER (ORDER BY DateFrom) RowId
        FROM @T
    )
    SELECT
        A.SeasonId, A.DateTo,
        B.SeasonId, B.DateFrom
    FROM T A JOIN T B ON A.RowId + 1 = B.RowId
    WHERE A.DateTo <> B.DateFrom
    

    【讨论】:

      【解决方案4】:

      一种特别的蛮力方法:

      SELECT *
      FROM seasons s1
      CROSS JOIN seasons s2
      WHERE NOT (s1.date_to <= s2.date_from OR s1.date_from >= s2.date_to);
      

      希望没有足够的记录导致笛卡尔连接出现性能问题。如果您有 SQL Server 2012,则可以使用窗口函数对其进行改进(仅比较相邻季节)。

      【讨论】: