SQL Server find overlaping date ranges

2019-07-31 04:35发布

I have a table with date ranges (seasons) for each year. The normal is that the end of the one season is the beggining of the next season. In the example below I have in bold the two irregular season setups. In the first the end of season 1 is a day after the beggining of season 2. In the second, the beggining of season 4 is one day after the end of seaon 3

+--------+----+--------------+--------------+
|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  |
+--------+----+--------------+--------------+

Is there a way to write a query that can capture the seasons that are not correctly setup? (the ones that the end of one season is not the beginning of the next)

4条回答
Juvenile、少年°
2楼-- · 2019-07-31 04:38

An ad-hoc brute-force approach:

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

Hopefully there are not enough records to cause performance issues with cartesian join. If you have SQL Server 2012 it can be improved using window functions (only compare adjacent seasons).

查看更多
男人必须洒脱
3楼-- · 2019-07-31 04:39

I hope this will help you

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

where stest having the following detail

+--+----+----------+----------+
|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|
+--+----+----------+----------+

and the above query will give the following result

+--+----+----------+----------+--+----+----------+----------+
|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|
+--+----+----------+----------+--+----+----------+----------+

from the result you can get id ( 1,2 ) and (3,4) are mismatched

and if you have do like below

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

then you will get the matching result

+--+----+----------+----------+--+----+----------+----------+
|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|
+--+----+----------+----------+--+----+----------+----------+
查看更多
贪生不怕死
4楼-- · 2019-07-31 04:48
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
查看更多
手持菜刀,她持情操
5楼-- · 2019-07-31 04:56

This answers half of your question: use the overlapping date queries from this article to find conflicting records:

-- 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

Result:

+--------+----+----------+----------+--------+----+----------+----------+
|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

查看更多
登录 后发表回答