差距和岛屿

问题描述

我有一个带有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 BYPlaceID子句,但是由于样本基于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;