问题描述
我有一个带有StartDate和EndDate的表。我想获取缺少的日期,而我拥有的脚本做到了。但是例如
LocationID StartDate EndDate
1 2020-01-01 2020-01-03
1 2020-01-04 2020-01-05
1 2020-01-10 2020-01-15
DECLARE @t table(PlaceID int,StartDate date,EndDate date);
INSERT @t(PlaceID,StartDate,EndDate) VALUES
(1,'20200101','20200103'),(1,'20200104','20200105'),'20200110','20200115'),--(2,'20200103','20200106'),(2,'20200107','20200110'),'20200120','20200123');
-- input parameters
DECLARE @PlaceIDofInterest int = 1,@StartDateOfInterest date = '20200101',@EndDateOfInterest date = '20200131';
;WITH date_range(d) AS -- the entire range of days we care about
(
SELECT @StartDateOfInterest UNION ALL
SELECT DATEADD(DAY,1,d) FROM date_range
WHERE d < @EndDateOfInterest
),islands AS -- grouped sets of days _not_ covered
(
SELECT r.d,island = DATEADD(DAY,DENSE_RANK() OVER (ORDER BY r.d) * -1,r.d)
FROM date_range AS r
LEFT OUTER JOIN @t AS t
ON r.d >= t.StartDate
AND r.d <= t.EndDate
AND t.PlaceID = @PlaceIDofInterest
WHERE t.PlaceID IS NULL
)
SELECT MIN(d) as STARTDATE,MAX(d) as ENDDATE-- for each island,grab the start and end
FROM islands
GROUP BY island
ORDER BY MIN(d);
我得到的输出是
STARTDATE ENDDATE
2020-01-06 2020-01-09
2020-01-16 2020-01-31
但是我希望输出为
STARTDATE ENDDATE
2020-01-03 2020-01-04
2020-01-05 2020-01-10
2020-01-15 2020-01-31
我可以进行哪些更改?
可以从晚上到早上预订位置。因此,从StartDate Night到EndDate Morning。因此,除非已预订,否则该位置将在EndDate晚上免费使用。此外,该位置还将在开始日期早上免费提供。
1月1日至1月3日被预订了-这意味着从1号晚上到3号早上。下次预订仅在第4晚。因此,该位置在第3夜至第4早晨也是免费的。所以我需要结果在一行中有3rd作为开始日期,4thJan作为结束日期。希望这很清楚。
谢谢
解决方法
我不会撒谎,我可能对此有点过分复杂,但这是我走过的方向,所以我一直看到底。但是,我确实用Tally代替了您的rCTE,对于更大的集,它应该(明显)具有更高的性能。您可能还需要在PARTITION BY
上添加GROUP BY
和PlaceID
子句,但是由于样本基于1个地方,因此我没有实现它。
DECLARE @t table(PlaceID int,StartDate date,EndDate date);
INSERT @t(PlaceID,StartDate,EndDate) VALUES
(1,'20200101','20200103'),(1,'20200104','20200105'),'20200110','20200115');
--(2,'20200103','20200106'),(2,'20200107','20200110'),'20200120','20200123');
-- input parameters
DECLARE @PlaceIDofInterest int = 1,@StartDateOfInterest date = '20200101',@EndDateOfInterest date = '20200131';
WITH N AS(
SELECT N
FROM (VALUES(NULL),(NULL),(NULL))N(N)),Tally AS(
SELECT TOP(DATEDIFF(DAY,@StartDateOfInterest,@EndDateOfInterest)+1)
ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) - 1 AS I
FROM N N1,N N2,N N3),--1000 days
Dates AS(
SELECT DATEADD(DAY,I,@StartDateOfInterest) AS [Date]
FROM Tally),Gaps AS(
SELECT D.[Date],CASE LAG(DATEADD(DAY,1,D.[Date]),D.[Date]) OVER (ORDER BY D.[Date]) WHEN D.Date THEN 1 ELSE 0 END AS [Check]
FROM Dates D
LEFT JOIN @t t ON D.[Date] >= t.StartDate
AND D.[Date] < t.EndDate
WHERE t.PlaceID IS NULL),Grps AS(
SELECT [Date],COUNT(CASE [Check] WHEN 0 THEN 1 END) OVER (ORDER BY [Date] ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS Grp
FROM Gaps)
SELECT MIN([Date]) AS StartDate,MAX(CASE [Date] WHEN @EndDateOfInterest THEN [Date] ELSE DATEADD(DAY,[Date]) END) AS EndDate
FROM Grps
GROUP BY Grp;