Lacune e isole
Ho una tabella con StartDate e EndDate. Volevo ottenere la data mancante e lo script che ho lo fa. Ma se vengono forniti questi dati:
LocationID StartDate EndDate
---------- ---------- ----------
1 2020-01-01 2020-01-03
1 2020-01-04 2020-01-05
1 2020-01-10 2020-01-15
Questa domanda:
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);
Restituisce questo output:
STARTDATE ENDDATE
--------- ----------
2020-01-06 2020-01-09
2020-01-16 2020-01-31
Ma volevo l'output come:
STARTDATE ENDDATE
--------- ----------
2020-01-03 2020-01-04
2020-01-05 2020-01-10
2020-01-15 2020-01-31
Quali modifiche posso apportare?
Modificato per aggiungere: una posizione può essere prenotata dalla notte alla mattina. Quindi StartDate Night to EndDate Morning. Quindi la posizione sarà libera la notte di EndDate, a meno che non sia prenotata. Anche la posizione sarà libera in Startdate Morning.
È prenotato dal 1 ° gennaio al 3 gennaio, ovvero dalla prima notte alla terza mattina. la prossima prenotazione è solo la 4a notte. Quindi la location è gratuita anche dalla terza notte alla quarta mattina. Quindi avrei bisogno che il risultato avesse il 3 ° come data di inizio e il 4 ° gennaio come data di fine in una riga. Spero che questo sia chiaro.
Risposte
Non hai davvero bisogno di un cte per questo
La funzione finestra LEAD può fare ciò di cui hai bisogno
Può essere raffinato se le date corrispondono
CREATE table t1(PlaceID int, StartDate date, EndDate date);
GO
INSERT INTO t1 (PlaceID, StartDate, EndDate) VALUES (1,'20200101','20200103') ,(1,'20200104','20200111') ,(1,'20200111','20200113') ,(1,'20200114','20200121') ,(1,'20200121','20200123') ,(2,'20200103','20200106') ,(2,'20200107','20200110') ,(2,'20200120','20200123'); GO
SELECT PlaceID ,EndDate ,Nextdate FROM (SELECT PlaceID ,EndDate, COALESCE(DATEADD(DAY,0,lead(StartDate,1,NULL) OVER (partition by PlaceID ORDER BY StartDate)),EOMONTH(StartDate)) AS Nextdate FROM t1) t2 WHERE EndDate != Nextdate GOPlaceID | EndDate | Nextdate ------: | : --------- | : --------- 1 | 03-01-2020 | 2020-01-04 1 | 13/01/2020 | 14-01-2020 1 | 23/01/2020 | 2020-01-31 2 | 06-01-2020 | 2020-01-07 2 | 10/01/2020 | 2020-01-20 2 | 23/01/2020 | 2020-01-31
db <> fiddle qui
La risposta di @nbk ti darà quello che vuoi, lo metto qui perché potrebbe rivelarsi interessante per te; sebbene non sia completo. Al momento, ottiene l'elenco delle possibili date di prenotazione e mostra se è stata prenotata o meno in base alle regole che hai delineato. Potrebbe essere utile come visualizzazione del calendario o qualcosa di simile per te.
DECLARE @Source TABLE
(
LocationID INT NOT NULL
, StartDate DATE NOT NULL
, EndDate DATE NOT NULL
)
INSERT INTO @Source
(LocationID, StartDate, EndDate)
VALUES (1, '1/1/2020', '1/3/2020')
, (1, '1/4/2020', '1/5/2020')
, (1, '1/10/2020', '1/15/2020')
DECLARE @LocationOfInterest INT = 1
DECLARE @StartDate DATE = '1/1/2020'
DECLARE @EndDate DATE = '1/31/2020'
;WITH CTE_I AS
(
SELECT *
, PriorEndDate = LAG(EndDate) OVER (PARTITION BY LocationID ORDER BY StartDate)
, NextStart = LEAD(StartDate) OVER (PARTITION BY LocationID ORDER BY StartDate)
FROM @Source AS S
WHERE LocationID = @LocationOfInterest
)
, CTE_G AS
(
SELECT I.LocationID
, I.StartDate
, I.EndDate
, I.PriorEndDate
, I.NextStart
, IslandStart = COALESCE(PriorEndDate, EndDate)
, IslandEnd = I.NextStart
FROM CTE_I AS I
)
SELECT * FROM CTE_G
;WITH CTE_Calendar AS
(
SELECT @StartDate AS D
UNION ALL
SELECT DATEADD(DAY, 1, D)
FROM CTE_Calendar
WHERE D < @EndDate
)
, CTE_BookingCalendar AS
(
SELECT C.D AS BookingStart
, DATEADD(DAY, 1, C.D) AS BookingEnd
FROM CTE_Calendar AS C
)
, CTE_WhatIsBooked AS
(
SELECT B.BookingStart
, B.BookingEnd
, S.LocationID
, S.StartDate
, S.EndDate
FROM CTE_BookingCalendar AS B
LEFT OUTER JOIN @Source AS S ON B.BookingStart BETWEEN S.StartDate AND S.EndDate AND S.EndDate >= B.BookingEnd
WHERE S.LocationID IS NULL OR S.LocationID = @LocationOfInterest
ORDER BY B.BookingStart
)