sql >> Database teknologi >  >> RDS >> Sqlserver

Tally Tabel for at indsætte manglende datoer mellem to datoer? SQL

Eksempel på data

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');
 

Forespørgsel

Forespørgsel bruger CROSS APPLY at "indsætte" rækker, når der er et hul i datoer. Den dublerer den aktuelle række så mange gange som nødvendigt ved hjælp af din Tally tabel med tal.

Der er en særlig håndtering af sagen, når @StartDate er før datoen for første række. Det er derfor, der er to SELECTs forenet sammen.

CTE.PrevDate IS NULL filtrerer kun sådanne rækker, og de gentages så mange gange som nødvendigt.

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;
 

Resultat

+----------+------------+------------+-----------+---------+-------------------------+ | 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 | +----------+------------+------------+-----------+---------+-------------------------+

Indsættelse af de genererede rækker tilbage i den oprindelige tabel

Først var jeg ikke klar over, at du ville ændre den originale tabel, så jeg skrev en SELECT forespørgsel, der returnerer et nødvendigt resultatsæt. Det er nemt at justere det til INSERT forespørgsel, der ville tilføje nye rækker til den oprindelige tabel.

Alt jeg gjorde var at tilføje et filter WHERE NewDate <> dt , som sikrer, at kun nye rækker, der ikke eksisterede før, indsættes.

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;
 

Resultat

For at kontrollere resultatet SELECT alt fra den originale tabel.

SELECT * FROM @Table_One ORDER BY Staff_ID, dt;
 

Resultatet er det samme som vist ovenfor.



  1. Regneark vs. databaser:Er det tid til at skifte? Del 1

  2. Oracle:SQL-forespørgsel, der returnerer rækker med kun numeriske værdier

  3. Problemsæt 2 – Identifikation af enheder og attributter

  4. Postgres heltalsarrays som parametre?