差距和岛屿

Gaps and Island

我有一个带有 StartDate 和 EndDate 的 table。我想得到丢失的日期和我的脚本。但是比如

位置 ID 开始日期结束日期

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'),(1,'20200110','20200115'),
--(2,'20200103','20200106'),(2,'20200107','20200110'),(2,'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);

我得到的输出是,

开始日期结束日期

2020-01-06 2020-01-09

2020-01-16 2020-01-31

但我希望输出为,

开始日期结束日期

2020-01-03 2020-01-04

2020-01-05 2020-01-10

2020-01-15 2020-01-31

我可以做哪些改变?

一个位置可以从晚上预定到早上。所以 StartDate 晚上到 EndDate 早上。因此,该位置在 EndDate 之夜将免费,除非已被预订。此外,位置将在开始日期早上免费。

1 月 1 日至 1 月 3 日已预订 - 即从第 1 晚到第 3 天早上。下一次预订仅在第 4 晚。因此,该位置从第 3 晚到第 4 天早上也是免费的。所以我需要结果在一行中有第 3 个作为开始日期和 4thJan 作为结束日期。希望这是清楚的。

谢谢

我不会说谎,我可能把它复杂化了一点,但这是我的方向,所以我看到了最后。但是,我确实将您的 rCTE 替换为 Tally,对于更大的集合,它应该(显着)更高效。您可能还需要在 PlaceID 上添加 PARTITION BYGROUP BY 子句,但由于示例基于 1 个地方,因此我没有实现它。

DECLARE @t table(PlaceID int, StartDate date, EndDate date);

INSERT @t(PlaceID, StartDate, EndDate) VALUES
(1,'20200101','20200103'),(1,'20200104','20200105'),(1,'20200110','20200115');
--(2,'20200103','20200106'),(2,'20200107','20200110'),(2,'20200120','20200123');


-- input parameters 
DECLARE @PlaceIDofInterest   int  = 1,
        @StartDateOfInterest date = '20200101', 
        @EndDateOfInterest   date = '20200131';
    
WITH N AS(
    SELECT N
    FROM (VALUES(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(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]),1,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,1,[Date]) END) AS EndDate
FROM Grps
GROUP BY Grp;