我为此付出了几天的努力...尝试编写一个SQL查询以在所有单位同时重叠时获取所有日期范围。最好以图形方式查看它。
这是带有图像的简化表,以供参考:
UnitId Start End
====== ========== ==========
1 05/01/2018 09/01/2018
1 10/01/2018 13/01/2018
2 04/01/2018 15/01/2018
2 19/01/2018 23/01/2018
3 06/01/2018 12/01/2018
3 14/01/2018 22/01/2018
预期结果:
Start End
====== ==========
06/01/2018 09/01/2018
10/01/2018 12/01/2018
我目前拥有的东西:
DECLARE @sourceTable TABLE (UnitId int, StartDate datetime, EndDate datetime);
INSERT INTO @sourceTable VALUES
(1, '2018-01-05', '2018-01-09')
,(1, '2018-01-10', '2018-01-13')
,(2, '2018-01-04', '2018-01-15')
,(2, '2018-01-19', '2018-01-23')
,(3, '2018-01-06', '2018-01-12')
,(3, '2018-01-14', '2018-01-22');
SELECT DISTINCT
(SELECT max(v) FROM (values(A.StartDate), (B.StartDate)) as value(v)) StartDate
,(SELECT min(v) FROM (values(A.EndDate), (B.EndDate)) as value(v)) EndDate
FROM @sourceTable A
JOIN @sourceTable B
ON A.startDate <= B.endDate AND A.endDate >= B.startDate AND A.UnitId != B.UnitId
答案 0 :(得分:3)
我认为这是“重叠间隔数”问题(this picture should help)。这是一种解决方案:
DECLARE @t TABLE (UnitId INT, [Start] DATE, [End] DATE);
INSERT INTO @t VALUES
(1, '2018-01-05', '2018-01-09'),
(1, '2018-01-10', '2018-01-13'),
(2, '2018-01-04', '2018-01-15'),
(2, '2018-01-19', '2018-01-23'),
(3, '2018-01-06', '2018-01-12'),
(3, '2018-01-14', '2018-01-22');
WITH cte1(date, val) AS (
SELECT [Start], 1 FROM @t AS t
UNION ALL
SELECT [End], 0 FROM @t AS t
UNION ALL
SELECT DATEADD(DAY, 1, [End]), -1 FROM @t AS t
), cte2 AS (
SELECT date, SUM(val) OVER (ORDER BY date, val) AS usage
FROM cte1
)
SELECT date, MAX(usage) AS usage
FROM cte2
GROUP BY date
它将为您提供使用次数(可能)发生更改的所有日期的列表:
date usage
2018-01-04 1
2018-01-05 2
2018-01-06 3
2018-01-09 3
2018-01-10 3
2018-01-12 3
2018-01-13 2
2018-01-14 2
2018-01-15 2
2018-01-16 1
2018-01-19 2
2018-01-22 2
2018-01-23 1
2018-01-24 0
使用这种方法,您不需要日历表或rCTE即可构建缺少的日期。将上述内容转换为范围(2018-01-05 ... 2018-01-15
,2018-01-19 ... 2018-01-22
等)并不是很困难。
DECLARE @t TABLE (UnitId INT, [Start] DATE, [End] DATE);
INSERT INTO @t VALUES
(1, '2018-01-05', '2018-01-09'),
(1, '2018-01-10', '2018-01-13'),
(2, '2018-01-04', '2018-01-15'),
(2, '2018-01-19', '2018-01-23'),
(3, '2018-01-06', '2018-01-12'),
(3, '2018-01-14', '2018-01-22');
WITH cte1(date, val) AS (
SELECT [Start], 1 FROM @t AS t -- starting date increments counter
UNION ALL
SELECT [End], 0 FROM @t AS t -- we need all edges in the result
UNION ALL
SELECT DATEADD(DAY, 1, [End]), -1 FROM @t AS t -- end date + 1 decrements counter
), cte2 AS (
SELECT date, SUM(val) OVER (ORDER BY date, val) AS usage -- running sum for counter
FROM cte1
), cte3 AS (
SELECT date, MAX(usage) AS usage -- group multiple events on same date together
FROM cte2
GROUP BY date
), cte4 AS (
SELECT date, usage, CASE
WHEN usage > 1 AND LAG(usage) OVER (ORDER BY date) > 1 THEN 0
WHEN usage < 2 AND LAG(usage) OVER (ORDER BY date) < 2 THEN 0
ELSE 1
END AS chg -- start new group if prev and curr usage are on opposite side of 1
FROM cte3
), cte5 AS (
SELECT date, usage, SUM(chg) OVER (ORDER BY date) AS grp -- number groups for each change
FROM cte4
)
SELECT MIN(date) date1, MAX(date) date2
FROM cte5
GROUP BY grp
HAVING MIN(usage) > 1
结果:
date1 date2
2018-01-05 2018-01-15
2018-01-19 2018-01-22
答案 1 :(得分:0)
您正在寻找所有单位重叠的日期范围。因此,寻找所有单元均存在的开始日期和所有单元均存在的结束日期,然后将两者合并。
我正在使用ROW_NUMBER
将第一个开始日期与第一个结束日期,第二个开始日期与第二个结束日期等相连接。
select s.startdate, e.enddate
from
(
select startdate, row_number() over (order by startdate) as rn
from @sourceTable s1
where
(
select count(*)
from @sourceTable s2
where s1.startdate between s2.startdate and s2.enddate
) = (select count(distinct unitid) from @sourceTable)
) s
join
(
select enddate, row_number() over (order by startdate) as rn
from @sourceTable s1
where
(
select count(*)
from @sourceTable s2
where s1.enddate between s2.startdate and s2.enddate
) = (select count(distinct unitid) from @sourceTable)
) e on e.rn = s.rn
order by s.startdate;
也许有更优雅的方法可以解决此问题,但是我想这个查询至少很容易理解:-)