Przykładowe dane
DECLARE @StartDate date = '2016-06-01';
DECLARE @EndDate date = '2016-07-01';
DECLARE @Table_One TABLE (
Staff_ID int,
dt date,
First_Name nvarchar(50),
Last_Name nvarchar(50),
Section nvarchar(50),
Time_Worked datetime);
INSERT INTO @Table_One(Staff_ID, dt, First_Name, Last_Name, Section, Time_Worked)
VALUES
(1001, '2016-06-01', 'Bill', 'Price ', 'Level 1', '2016-06-01 8:30:00.000'),
(1001, '2016-06-05', 'Bill', 'Price ', 'Level 1', '2016-06-05 8:30:00.000'),
(1001, '2016-06-09', 'Bill', 'Price ', 'Level 1', '2016-06-09 8:30:00.000'),
(1001, '2016-06-12', 'Bill', 'Price ', 'Level 1', '2016-06-12 8:30:00.000'),
(1002, '2016-06-01', 'Mary', 'Somers', 'Level 1', '2016-06-01 8:30:00.000'),
(1002, '2016-06-05', 'Mary', 'Somers', 'Level 1', '2016-06-05 8:30:00.000'),
(1002, '2016-06-08', 'Mary', 'Somers', 'Level 1', '2016-06-08 8:30:00.000'),
(1003, '2016-06-03', 'Mark', 'Jones ', 'Level 1', '2016-06-03 8:30:00.000'),
(1003, '2016-06-05', 'Mark', 'Jones ', 'Level 1', '2016-06-05 8:30:00.000');
Zapytanie
Zapytanie używa CROSS APPLY
aby „wstawić” wiersze, gdy istnieje luka w datach. Powiela bieżący wiersz tyle razy, ile potrzeba, używając Twojego Tally
tabela liczb.
Istnieje specjalna obsługa przypadku, gdy @StartDate
przypada przed datą pierwszego rzędu. Dlatego są dwa SELECTs
razem.
CTE.PrevDate IS NULL
filtruje tylko takie wiersze i powtarza się je tyle razy, ile potrzeba.
WITH
CTE
AS
(
SELECT *
,LAG(dt) OVER (PARTITION BY Staff_ID ORDER BY dt) AS PrevDate
,LEAD(dt) OVER (PARTITION BY Staff_ID ORDER BY dt) AS NextDate
FROM @Table_One AS T
)
SELECT
Staff_ID
,NewDate
,First_Name
,Last_Name
,Section
,CASE WHEN NewDate = dt THEN Time_Worked ELSE NULL END AS Time_Worked
FROM
CTE
CROSS APPLY
(
SELECT DATEADD(day, Tally.ID - 1, CTE.dt) AS NewDate
FROM dbo.Tally
WHERE Tally.ID <= DATEDIFF(day, CTE.dt, ISNULL(CTE.NextDate, @EndDate))
) AS CA_Next
UNION ALL
SELECT
Staff_ID
,NewDate
,First_Name
,Last_Name
,Section
,CASE WHEN NewDate = dt THEN Time_Worked ELSE NULL END AS Time_Worked
FROM
CTE
CROSS APPLY
(
SELECT DATEADD(day, - Tally.ID, CTE.dt) AS NewDate
FROM dbo.Tally
WHERE Tally.ID <= DATEDIFF(day, @StartDate, CTE.dt)
) AS CA_Prev
WHERE
CTE.PrevDate IS NULL
ORDER BY Staff_ID, NewDate;
Wynik
+----------+------------+------------+-----------+---------+-------------------------+
| Staff_ID | NewDate | First_Name | Last_Name | Section | Time_Worked |
+----------+------------+------------+-----------+---------+-------------------------+
| 1001 | 2016-06-01 | Bill | Price | Level 1 | 2016-06-01 08:30:00.000 |
| 1001 | 2016-06-02 | Bill | Price | Level 1 | NULL |
| 1001 | 2016-06-03 | Bill | Price | Level 1 | NULL |
| 1001 | 2016-06-04 | Bill | Price | Level 1 | NULL |
| 1001 | 2016-06-05 | Bill | Price | Level 1 | 2016-06-05 08:30:00.000 |
| 1001 | 2016-06-06 | Bill | Price | Level 1 | NULL |
| 1001 | 2016-06-07 | Bill | Price | Level 1 | NULL |
| 1001 | 2016-06-08 | Bill | Price | Level 1 | NULL |
| 1001 | 2016-06-09 | Bill | Price | Level 1 | 2016-06-09 08:30:00.000 |
| 1001 | 2016-06-10 | Bill | Price | Level 1 | NULL |
| 1001 | 2016-06-11 | Bill | Price | Level 1 | NULL |
| 1001 | 2016-06-12 | Bill | Price | Level 1 | 2016-06-12 08:30:00.000 |
| 1001 | 2016-06-13 | Bill | Price | Level 1 | NULL |
| 1001 | 2016-06-14 | Bill | Price | Level 1 | NULL |
| 1001 | 2016-06-15 | Bill | Price | Level 1 | NULL |
| 1001 | 2016-06-16 | Bill | Price | Level 1 | NULL |
| 1001 | 2016-06-17 | Bill | Price | Level 1 | NULL |
| 1001 | 2016-06-18 | Bill | Price | Level 1 | NULL |
| 1001 | 2016-06-19 | Bill | Price | Level 1 | NULL |
| 1001 | 2016-06-20 | Bill | Price | Level 1 | NULL |
| 1001 | 2016-06-21 | Bill | Price | Level 1 | NULL |
| 1001 | 2016-06-22 | Bill | Price | Level 1 | NULL |
| 1001 | 2016-06-23 | Bill | Price | Level 1 | NULL |
| 1001 | 2016-06-24 | Bill | Price | Level 1 | NULL |
| 1001 | 2016-06-25 | Bill | Price | Level 1 | NULL |
| 1001 | 2016-06-26 | Bill | Price | Level 1 | NULL |
| 1001 | 2016-06-27 | Bill | Price | Level 1 | NULL |
| 1001 | 2016-06-28 | Bill | Price | Level 1 | NULL |
| 1001 | 2016-06-29 | Bill | Price | Level 1 | NULL |
| 1001 | 2016-06-30 | Bill | Price | Level 1 | NULL |
| 1002 | 2016-06-01 | Mary | Somers | Level 1 | 2016-06-01 08:30:00.000 |
| 1002 | 2016-06-02 | Mary | Somers | Level 1 | NULL |
| 1002 | 2016-06-03 | Mary | Somers | Level 1 | NULL |
| 1002 | 2016-06-04 | Mary | Somers | Level 1 | NULL |
| 1002 | 2016-06-05 | Mary | Somers | Level 1 | 2016-06-05 08:30:00.000 |
| 1002 | 2016-06-06 | Mary | Somers | Level 1 | NULL |
| 1002 | 2016-06-07 | Mary | Somers | Level 1 | NULL |
| 1002 | 2016-06-08 | Mary | Somers | Level 1 | 2016-06-08 08:30:00.000 |
| 1002 | 2016-06-09 | Mary | Somers | Level 1 | NULL |
| 1002 | 2016-06-10 | Mary | Somers | Level 1 | NULL |
| 1002 | 2016-06-11 | Mary | Somers | Level 1 | NULL |
| 1002 | 2016-06-12 | Mary | Somers | Level 1 | NULL |
| 1002 | 2016-06-13 | Mary | Somers | Level 1 | NULL |
| 1002 | 2016-06-14 | Mary | Somers | Level 1 | NULL |
| 1002 | 2016-06-15 | Mary | Somers | Level 1 | NULL |
| 1002 | 2016-06-16 | Mary | Somers | Level 1 | NULL |
| 1002 | 2016-06-17 | Mary | Somers | Level 1 | NULL |
| 1002 | 2016-06-18 | Mary | Somers | Level 1 | NULL |
| 1002 | 2016-06-19 | Mary | Somers | Level 1 | NULL |
| 1002 | 2016-06-20 | Mary | Somers | Level 1 | NULL |
| 1002 | 2016-06-21 | Mary | Somers | Level 1 | NULL |
| 1002 | 2016-06-22 | Mary | Somers | Level 1 | NULL |
| 1002 | 2016-06-23 | Mary | Somers | Level 1 | NULL |
| 1002 | 2016-06-24 | Mary | Somers | Level 1 | NULL |
| 1002 | 2016-06-25 | Mary | Somers | Level 1 | NULL |
| 1002 | 2016-06-26 | Mary | Somers | Level 1 | NULL |
| 1002 | 2016-06-27 | Mary | Somers | Level 1 | NULL |
| 1002 | 2016-06-28 | Mary | Somers | Level 1 | NULL |
| 1002 | 2016-06-29 | Mary | Somers | Level 1 | NULL |
| 1002 | 2016-06-30 | Mary | Somers | Level 1 | NULL |
| 1003 | 2016-06-01 | Mark | Jones | Level 1 | NULL |
| 1003 | 2016-06-02 | Mark | Jones | Level 1 | NULL |
| 1003 | 2016-06-03 | Mark | Jones | Level 1 | 2016-06-03 08:30:00.000 |
| 1003 | 2016-06-04 | Mark | Jones | Level 1 | NULL |
| 1003 | 2016-06-05 | Mark | Jones | Level 1 | 2016-06-05 08:30:00.000 |
| 1003 | 2016-06-06 | Mark | Jones | Level 1 | NULL |
| 1003 | 2016-06-07 | Mark | Jones | Level 1 | NULL |
| 1003 | 2016-06-08 | Mark | Jones | Level 1 | NULL |
| 1003 | 2016-06-09 | Mark | Jones | Level 1 | NULL |
| 1003 | 2016-06-10 | Mark | Jones | Level 1 | NULL |
| 1003 | 2016-06-11 | Mark | Jones | Level 1 | NULL |
| 1003 | 2016-06-12 | Mark | Jones | Level 1 | NULL |
| 1003 | 2016-06-13 | Mark | Jones | Level 1 | NULL |
| 1003 | 2016-06-14 | Mark | Jones | Level 1 | NULL |
| 1003 | 2016-06-15 | Mark | Jones | Level 1 | NULL |
| 1003 | 2016-06-16 | Mark | Jones | Level 1 | NULL |
| 1003 | 2016-06-17 | Mark | Jones | Level 1 | NULL |
| 1003 | 2016-06-18 | Mark | Jones | Level 1 | NULL |
| 1003 | 2016-06-19 | Mark | Jones | Level 1 | NULL |
| 1003 | 2016-06-20 | Mark | Jones | Level 1 | NULL |
| 1003 | 2016-06-21 | Mark | Jones | Level 1 | NULL |
| 1003 | 2016-06-22 | Mark | Jones | Level 1 | NULL |
| 1003 | 2016-06-23 | Mark | Jones | Level 1 | NULL |
| 1003 | 2016-06-24 | Mark | Jones | Level 1 | NULL |
| 1003 | 2016-06-25 | Mark | Jones | Level 1 | NULL |
| 1003 | 2016-06-26 | Mark | Jones | Level 1 | NULL |
| 1003 | 2016-06-27 | Mark | Jones | Level 1 | NULL |
| 1003 | 2016-06-28 | Mark | Jones | Level 1 | NULL |
| 1003 | 2016-06-29 | Mark | Jones | Level 1 | NULL |
| 1003 | 2016-06-30 | Mark | Jones | Level 1 | NULL |
+----------+------------+------------+-----------+---------+-------------------------+
Wstawianie wygenerowanych wierszy z powrotem do oryginalnej tabeli
Na początku nie zdawałem sobie sprawy, że chcesz zmienić oryginalną tabelę, więc napisałem SELECTs
zapytanie, które zwraca wymagany zestaw wyników. Łatwo jest dostosować go do INSERT
zapytanie, które doda nowe wiersze do oryginalnej tabeli.
Wszystko, co zrobiłem, to dodałem filtr WHERE NewDate <> dt
, co zapewnia wstawianie tylko nowych wierszy, które wcześniej nie istniały.
WITH
CTE
AS
(
SELECT
Staff_ID
,dt
,First_Name
,Last_Name
,Section
,Time_Worked
,LAG(dt) OVER (PARTITION BY Staff_ID ORDER BY dt) AS PrevDate
,LEAD(dt) OVER (PARTITION BY Staff_ID ORDER BY dt) AS NextDate
FROM @Table_One AS T
)
INSERT INTO @Table_One(Staff_ID, dt, First_Name, Last_Name, Section, Time_Worked)
SELECT
Staff_ID
,NewDate
,First_Name
,Last_Name
,Section
,NULL AS Time_Worked
FROM
CTE
CROSS APPLY
(
SELECT DATEADD(day, Tally.ID - 1, CTE.dt) AS NewDate
FROM dbo.Tally
WHERE Tally.ID <= DATEDIFF(day, CTE.dt, ISNULL(CTE.NextDate, @EndDate))
) AS CA_Next
WHERE
NewDate <> dt
UNION ALL
SELECT
Staff_ID
,NewDate
,First_Name
,Last_Name
,Section
,NULL AS Time_Worked
FROM
CTE
CROSS APPLY
(
SELECT DATEADD(day, - Tally.ID, CTE.dt) AS NewDate
FROM dbo.Tally
WHERE Tally.ID <= DATEDIFF(day, @StartDate, CTE.dt)
) AS CA_Prev
WHERE
CTE.PrevDate IS NULL
ORDER BY Staff_ID, NewDate;
Wynik
Aby sprawdzić wynik, po prostu SELECT
wszystko z oryginalnego stołu.
SELECT * FROM @Table_One ORDER BY Staff_ID, dt;
Wynik jest taki sam, jak pokazano powyżej.