简体   繁体   中英

How to count the Gap between dates in SQL Server with overlapping dates

I'm trying to get the GAP (In days) between records in one Table, I'm using SQL Server.

So far, I was able to get the gap (in days) between dates, but there was an issue with the overlapping dates.

Here is the setup script:

CREATE TABLE #Temp
(
   RoomTypeId INT,
   BedId INT,
   StartDate DATE,
   EndDate DATE
)

INSERT INTO #Temp (RoomTypeId, BedId, StartDate, EndDate)
VALUES (39, 229, '2019-08-03', '2019-08-04'),
       (39, 229, '2019-08-05', '2019-08-08'),
       (40, 231, '2019-08-05', '2019-08-08'),
       (41, 238, '2019-08-02', '2019-08-03'),
       (42, 233, '2019-08-02', '2019-08-03'),
       (42, 233, '2019-08-04', '2019-08-05'),
       (42, 234, '2019-08-05', '2019-08-05'),
       (43, 241, '2019-08-01', '2019-08-01'),
       (43, 241, '2019-08-15', '2019-08-16'),
       (43, 241, '2019-08-29', '2019-08-30'),
       (45, 245, '2019-08-03', '2019-08-06'),
       (45, 244, '2019-08-05', '2019-08-05'),
       (45, 244, '2019-08-08', '2019-08-10'),
       (45, 244, '2019-08-17', '2019-08-19'),
       (46, 239, '2019-08-03', '2019-08-04'),
       (46, 239, '2019-08-05', '2019-08-05'),
       (48, 250, '2019-08-16', '2019-08-18'),
       (49, 274, '2019-08-06', '2019-08-11'),
       (49, 275, '2019-08-08', '2019-08-10');

Here is my query:

SELECT 
    p.RoomTypeId,
    p.EndDateSeq1,
    p.StartDateSeq2,
    SUM(p.GAP) AS [GAP]
FROM 
    (SELECT 
         t.RoomTypeId,
         t.EndDate AS [EndDateSeq1],
         t2.StartDate AS [StartDateSeq2],
         DATEDIFF(d, t.EndDate,t2.StartDate) AS [GAP]
     FROM 
         #Temp t
     LEFT JOIN 
         #Temp t2 ON t2.RoomTypeId = t.RoomTypeId
                  AND (t2.StartDate > t.EndDate OR t2.StartDate = t.EndDate)) p
GROUP BY 
    p.RoomTypeId, p.EndDateSeq1, p.StartDateSeq2
ORDER BY 
    p.RoomTypeId, p.EndDateSeq1 ASC

Result:

╔════════════╦═════════════╦═══════════════╦══════╗
║ RoomTypeId ║ EndDateSeq1 ║ StartDateSeq2 ║ GAP  ║
╠════════════╬═════════════╬═══════════════╬══════╣
║         39 ║ 2019-08-04  ║ 2019-08-05    ║ 1    ║
║         39 ║ 2019-08-08  ║ NULL          ║ NULL ║
║         40 ║ 2019-08-08  ║ NULL          ║ NULL ║
║         41 ║ 2019-08-03  ║ NULL          ║ NULL ║
║         42 ║ 2019-08-03  ║ 2019-08-04    ║ 1    ║
║         42 ║ 2019-08-03  ║ 2019-08-05    ║ 2    ║
║         42 ║ 2019-08-05  ║ 2019-08-05    ║ 0    ║
║         43 ║ 2019-08-01  ║ 2019-08-01    ║ 0    ║
║         43 ║ 2019-08-01  ║ 2019-08-15    ║ 14   ║
║         43 ║ 2019-08-01  ║ 2019-08-29    ║ 28   ║
║         43 ║ 2019-08-16  ║ 2019-08-29    ║ 13   ║
║         43 ║ 2019-08-30  ║ NULL          ║ NULL ║
║         45 ║ 2019-08-05  ║ 2019-08-05    ║ 0    ║
║         45 ║ 2019-08-05  ║ 2019-08-08    ║ 3    ║
║         45 ║ 2019-08-05  ║ 2019-08-17    ║ 12   ║
║         45 ║ 2019-08-06  ║ 2019-08-08    ║ 2    ║
║         45 ║ 2019-08-06  ║ 2019-08-17    ║ 11   ║
║         45 ║ 2019-08-10  ║ 2019-08-17    ║ 7    ║
║         45 ║ 2019-08-19  ║ NULL          ║ NULL ║
║         46 ║ 2019-08-04  ║ 2019-08-05    ║ 1    ║
║         46 ║ 2019-08-05  ║ 2019-08-05    ║ 0    ║
║         48 ║ 2019-08-18  ║ NULL          ║ NULL ║
║         49 ║ 2019-08-10  ║ NULL          ║ NULL ║
║         49 ║ 2019-08-11  ║ NULL          ║ NULL ║
╚════════════╩═════════════╩═══════════════╩══════╝

Expected Results:

╔════════════╦═════════════╦═══════════════╦══════╗
║ RoomTypeId ║ EndDateSeq1 ║ StartDateSeq2 ║ GAP  ║
╠════════════╬═════════════╬═══════════════╬══════╣
║         39 ║ 2019-08-04  ║ 2019-08-05    ║ 1    ║
║         39 ║ 2019-08-08  ║ NULL          ║ NULL ║
║         40 ║ 2019-08-08  ║ NULL          ║ NULL ║
║         41 ║ 2019-08-03  ║ NULL          ║ NULL ║
║         42 ║ 2019-08-03  ║ 2019-08-04    ║ 1    ║
║         42 ║ 2019-08-05  ║ NULL          ║ 0    ║
║         43 ║ 2019-08-01  ║ 2019-08-15    ║ 14   ║
║         43 ║ 2019-08-16  ║ 2019-08-29    ║ 13   ║
║         43 ║ 2019-08-30  ║ NULL          ║ NULL ║
║         45 ║ 2019-08-05  ║ 2019-08-06    ║ 1    ║
║         45 ║ 2019-08-06  ║ 2019-08-08    ║ 2    ║
║         45 ║ 2019-08-10  ║ 2019-08-17    ║ 7    ║
║         45 ║ 2019-08-19  ║ NULL          ║ NULL ║
║         46 ║ 2019-08-04  ║ 2019-08-05    ║ 1    ║
║         48 ║ 2019-08-18  ║ NULL          ║ NULL ║
║         49 ║ 2019-08-11  ║ NULL          ║ NULL ║
╚════════════╩═════════════╩═══════════════╩══════╝

The goal was to get the gap with respect to the overlapping dates

I don't quite follow your results.

I think the problem is a combination of gaps-and-islands along with "fill-in-gaps". That is, you need to combine the existing data that overlaps. Then you need to get the pieces in-between.

You can identify the overlaps by using a cumulative max of the end date from preceding rows. When it is less than or equal to the current start, then you have a new "island". The result is aggregation and lead() .

The code looks like this:

select roomTypeId, grp, max(enddate), lead(min(startdate)) over (partition by roomtypeid order by min(startdate)),
       datediff(day, max(enddate), lead(min(startdate)) over (partition by roomtypeid order by min(startdate)) )
from (select t.*,
             sum(case when prev_enddate > startdate then 0 else 1 end) over
                 (partition by RoomTypeId order by startdate) as grp
      from (select t.*,
                   max(enddate) over (partition by RoomTypeId
                                      order by startdate
                                      rows between unbounded preceding and 1 preceding
                                     ) as prev_enddate
            from temp t
           ) t
     ) t
group by roomtypeid, grp
order by roomtypeid, min(startdate);

Here is the db<>fiddle.

The results are subtly different, but they make more sense to me.

You mean something like that:

select
    main.*,
    iif(DATEDIFF(d, EndDate, nextStartDate)>0, DATEDIFF(d, EndDate, nextStartDate), 0) as GAP
from
(
    select
        RoomTypeId,
        lead(StartDate) over (partition by RoomTypeId order by StartDate asc) as nextStartDate,
        StartDate,
        EndDate
    from 
        tab1
) main;

sql fiddle: https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=94c1585c246e18b5d4bcc527997cdfc1

You can use ROW_Number() concept here.

Try this

SELECT 
    p.RoomTypeId,
    p.EndDateSeq1,
    p.StartDateSeq2,
    SUM(p.GAP) AS [GAP]
FROM (
    SELECT 
        t.RoomTypeId,
        t.EndDate AS [EndDateSeq1],
        t2.StartDate AS [StartDateSeq2],
        DATEDIFF(d, t.EndDate,t2.StartDate) AS [GAP],
        ROW_Number() OVER(PARTITION BY  t.RoomTypeId,t.StartDate ORDER By t2.StartDate) RN
    FROM #Temp t
    LEFT JOIN #Temp t2 ON t2.RoomTypeId = t.RoomTypeId 
        AND (t2.StartDate > t.EndDate) 
) p
WHERE RN = 1
GROUP BY p.RoomTypeId, p.EndDateSeq1, p.StartDateSeq2
ORDER BY p.RoomTypeId, p.EndDateSeq1 ASC

SQL Fiddle

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM