Sqlserver
 sql >> база данни >  >> RDS >> Sqlserver

Таблица за изчисление за вмъкване на липсващи дати между две дати? SQL

Примерни данни

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

Запитване

Заявката използва CROSS APPLY за "вмъкване" на редове, когато има празнина в датите. Той дублира текущия ред толкова пъти, колкото е необходимо, използвайки вашия Tally таблица с числа.

Има специална обработка на случая, когато @StartDate е преди датата на първия ред. Ето защо има два SELECTs обединени заедно.

CTE.PrevDate IS NULL филтрира само такива редове и те се повтарят толкова пъти, колкото е необходимо.

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;

Резултат

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

Вмъкване на генерираните редове обратно в оригиналната таблица

Първоначално не разбрах, че искате да промените оригиналната таблица, затова написах SELECT заявка, която връща необходимия набор от резултати. Лесно е да го настроите на INSERT заявка, която би добавила нови редове в оригиналната таблица.

Всичко, което направих, беше да добавим филтър WHERE NewDate <> dt , което гарантира, че се вмъкват само нови редове, които не са съществували преди.

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;

Резултат

За да проверите резултата, просто SELECT всичко от оригиналната таблица.

SELECT * FROM @Table_One ORDER BY Staff_ID, dt;

Резултатът е същият, както е показано по-горе.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Как да възстановите база данни на SQL Server на Mac с помощта на Azure Data Studio

  2. Най-ефективният начин в SQL Server за получаване на дата от дата+час?

  3. SQL групиране по месец и година

  4. Актуализирайте множество таблици в SQL Server с помощта на INNER JOIN

  5. SQL Server ПОКАЖЕ ТАБЛИЦИ Еквивалент