Определить количество пропущенных дат и рабочих дней в T- SQL - PullRequest
0 голосов
/ 16 апреля 2020

Я пытаюсь выяснить, как написать запрос, который добавляет несколько таблиц и подсчитывает пропущенные даты. В некоторых местах 5 рабочих дней / неделя (таблица X) и около 6 рабочих дней (таблица Y). Я хотел бы определить рабочие дни для местоположения, извлечь текущий счетчик и выяснить, на сколько дней некоторые из них отстают с заполнением данных. Я попробовал несколько примеров из SO с использованием UNION ALL, DATEDIFF и MAX текущего, но это не дает мне ожидаемого результата. Пропущенные строки - это просто иллюстрации пробелов. Самый простой запрос, тем лучше. Спасибо.

tableX

+-----------+----------+---------+-------+--------------+--------------+
|   Date    | Location | DayName | Count | BusinessDays | Missing Days |
+-----------+----------+---------+-------+--------------+--------------+
| 4/1/2020  | X        | Wed     |     1 |              |              |
| 4/2/2020  | X        | Thu     |     1 |              |              |
| 4/3/2020  | X        | Fri     |     1 |              |              |
| 4/4/2020  | X        | Sat     |       |              |              |
| 4/5/2020  | X        | Sun     |       |              |              |
| missing   |          | (Mon)   |       |              |              |
| missing   |          | (Tue)   |       |              |              |
| 4/8/2020  | X        | Wed     |     1 |              |              |
| 4/9/2020  | X        | Thu     |     1 |              |              |
| 4/10/2020 | X        | Fri     |     1 |              |              |
| 4/11/2020 | X        | Sat     |       |              |              |
| 4/12/2020 | X        | Sun     |       |              |              |
| 4/13/2020 | X        | Mon     |     1 |              |              |
| 4/14/2020 | X        | Tue     |     1 |              |              |
| 4/15/2020 | X        | Wed     |     1 |              |              |
|           |          | Total   |     9 |           11 |            2 |
+-----------+----------+---------+-------+--------------+--------------+

tableY

+-----------+----------+---------+-------+--------------+--------------+
|   Date    | Location | DayName | Count | BusinessDays | Missing Days |
+-----------+----------+---------+-------+--------------+--------------+
| 4/1/2020  | Y        | Wed     |     1 |              |              |
| 4/2/2020  | Y        | Thu     |     1 |              |              |
| 4/3/2020  | Y        | Fri     |     1 |              |              |
| 4/4/2020  | Y        | Sat     |     1 |              |              |
| 4/5/2020  | Y        | Sun     |       |              |              |
| missing   |          | (Mon)   |       |              |              |
| missing   |          | (Tue)   |       |              |              |
| missing   |          | (Wed)   |       |              |              |
| 4/9/2020  | Y        | Thu     |     1 |              |              |
| 4/10/2020 | Y        | Fri     |     1 |              |              |
| 4/11/2020 | Y        | Sat     |     1 |              |              |
| 4/12/2020 | Y        | Sun     |       |              |              |
| 4/13/2020 | Y        | Mon     |     1 |              |              |
| 4/14/2020 | Y        | Tue     |     1 |              |              |
| 4/15/2020 | Y        | Wed     |     1 |              |              |
|           |          | Total   |    10 |           13 |            3 |
+-----------+----------+---------+-------+--------------+--------------+

Желаемый результат

+-------+------+----------+--------------+
| Month | Year | Location | Missing Days |
+-------+------+----------+--------------+
|     4 | 2020 | X        |            2 |
|     4 | 2020 | Y        |            3 |
+-------+------+----------+--------------+

простой неудачный пример

DECLARE @StartDate DATETIME
DECLARE @EndDate DATETIME
SET @StartDate = (SELECT DATEADD(month, DATEDIFF(month, 0, GETDATE()), 0))
SET @EndDate = GETDATE()

SELECT [Location],
       [Date],
        (DATEDIFF(dd, @StartDate, @EndDate) + 1)
          -(DATEDIFF(wk, @StartDate, @EndDate) * 2)
          -(CASE WHEN DATENAME(dw, @StartDate) = 'Sunday' THEN 1 ELSE 0 END)
          -(CASE WHEN DATENAME(dw, @EndDate) = 'Saturday' THEN 1 ELSE 0 END) AS Businessdays
  FROM [Daily].[dbo].[tableX] WHERE YEAR(Date)=YEAR(getdate()) AND MONTH(Date)=MONTH(getdate())
  UNION ALL
SELECT [Location],
       [Date],
       (DATEDIFF(dd, @StartDate, @EndDate) + 1)
          -(DATEDIFF(wk, @StartDate, @EndDate) * 2)
          -(CASE WHEN DATENAME(dw, @StartDate) = 'Sunday' THEN 1 ELSE 0 END) AS Businessdays
  FROM [Daily].[dbo].[tableY] WHERE YEAR(Date)=YEAR(getdate()) AND MONTH(Date)=MONTH(getdate())

1 Ответ

1 голос
/ 17 апреля 2020

Этот ответ будет зависеть от использования таблицы чисел для генерации диапазона дат. Это может быть статически сгенерированная таблица чисел или CTE, однако из-за пределов рекурсии у CTE есть ограничения на число строк, которые вы можете сгенерировать.

Проверьте эти ссылки для получения информации о том, как построить таблицу чисел :

Stati c Таблица номеров

Последовательность номеров CTE

--SETUP FOR DEMO
DECLARE @tableX TABLE 
(
    Date DATETIME
    ,[Location] VARCHAR(1)
    ,DayOfWeekNumber INT
    ,DayofWeekName VARCHAR(10)
)

DECLARE @tableY TABLE 
(
    Date DATETIME
    ,[Location] VARCHAR(1)
    ,DayOfWeekNumber INT
    ,DayofWeekName VARCHAR(10)
)

INSERT INTO @tableX 
VALUES
(43920,'X',DATEPART(DW,43920),DATENAME(DW,43920)),
(43921,'X',DATEPART(DW,43921),DATENAME(DW,43921)),
(43922,'X',DATEPART(DW,43922),DATENAME(DW,43922)),
(43923,'X',DATEPART(DW,43923),DATENAME(DW,43923)),
(43924,'X',DATEPART(DW,43924),DATENAME(DW,43924)),
--(43925,'X',DATEPART(DW,43925),DATENAME(DW,43925)),
--(43926,'X',DATEPART(DW,43926),DATENAME(DW,43926)),
(43927,'X',DATEPART(DW,43927),DATENAME(DW,43927)),
(43928,'X',DATEPART(DW,43928),DATENAME(DW,43928)),
(43929,'X',DATEPART(DW,43929),DATENAME(DW,43929)),
(43930,'X',DATEPART(DW,43930),DATENAME(DW,43930)),
(43931,'X',DATEPART(DW,43931),DATENAME(DW,43931)),
(43932,'X',DATEPART(DW,43932),DATENAME(DW,43932)),
(43933,'X',DATEPART(DW,43933),DATENAME(DW,43933)),
(43934,'X',DATEPART(DW,43934),DATENAME(DW,43934))



INSERT INTO @tableY
VALUES
(43920,'Y',DATEPART(DW,43920),DATENAME(DW,43920)),
(43921,'Y',DATEPART(DW,43921),DATENAME(DW,43921)),
(43922,'Y',DATEPART(DW,43922),DATENAME(DW,43922)),
(43923,'Y',DATEPART(DW,43923),DATENAME(DW,43923)),
(43924,'Y',DATEPART(DW,43924),DATENAME(DW,43924)),
--(43925,'Y',DATEPART(DW,43925),DATENAME(DW,43925)),
--(43926,'Y',DATEPART(DW,43926),DATENAME(DW,43926)),
--(43927,'Y',DATEPART(DW,43927),DATENAME(DW,43927)),
(43928,'Y',DATEPART(DW,43928),DATENAME(DW,43928)),
(43929,'Y',DATEPART(DW,43929),DATENAME(DW,43929)),
(43930,'Y',DATEPART(DW,43930),DATENAME(DW,43930)),
(43931,'Y',DATEPART(DW,43931),DATENAME(DW,43931)),
(43932,'Y',DATEPART(DW,43932),DATENAME(DW,43932)),
(43933,'Y',DATEPART(DW,43933),DATENAME(DW,43933)),
(43934,'Y',DATEPART(DW,43934),DATENAME(DW,43934))

--END SETUP FOR DEMO

--DECLARE YOUR RANGE
DECLARE @rangeStart DATETIME
DECLARE @rangeEnd DATETIME

DECLARE @dataSet TABLE
(
    Date DATETIME
    ,[Location] VARCHAR(1)
    ,DayOfWeekNumber INT
    ,DayofWeekName VARCHAR(10)
)

--SIMPLE UNION ALL OF THE LOCATION TABLES IN QUESTION
--IF LOCATION CODE IS NOT ALREADY PART OF THE TABLE, YOU CAN DEFINE IT AS AN ADDITIONAL COLUMN IN YOUR STATEMENT
INSERT INTO @dataSet
select * from @tableX 
UNION ALL
SELECT * from @tableY

SELECT @rangeStart = MIN(DATE) FROM @dataSet
SELECT @rangeEnd = MAX(Date) FROM @dataSet




--DEFINE YOUR BUSINESS DAYS FOR EACH LOCATION
DECLARE @locationsBusinessDays TABLE
(
    ID INT NOT NULL IDENTITY(1,1) PRIMARY KEY
    ,LocationCode NVARCHAR(10)
    ,BusinessDay INT
)

--INSERT A RECORD FOR EACH LOCATION AND BUSINESS DAY FOR SAID LOCATION.
--THIS COULD BE A STATIC FACT TABLE IS YOU PREFER.
INSERT INTO @locationsBusinessDays
(
    LocationCode
    ,BusinessDay
)
VALUES
('X', 2)
,('X', 3)
,('X', 4)
,('X', 5)
,('X', 6)
,('Y', 2)
,('Y', 3)
,('Y', 4)
,('Y', 5)
,('Y', 6)
,('Y', 7)

--USE THE NUMBERS TABLE TO GENERATE A LIST OF DATES, ONE FOR EACH LOCATION, WITHIN THE DEFINED RANGE
;with reportRange
as
(
    select 
        CAST(Num as DATETIME) as DateStamp
        ,loc.LocationCode
        ,DATEPART(DW, n.num) As DayOfWeekNumber
        ,DATENAME(DW, n.Num) AS DayOfWeekName
    FROM Numbers n  
        CROSS APPLY (SELECT DISTINCT LocationCode From @locationsBusinessDays) loc
    WHERE n.Num >= @rangeStart ANd n.Num <= @rangeEnd
),
/*BRING ALL THE DATA BACK, 
  JOINING AGAINST YOUR BUSINESS DAYS FACT TABLE
  AND YOUR MERGED DATASET
  ANYTHING WITH A MISSING DATE BECAUSE OF THE LEFT JOIN ON THE MERGED DATASET 
  AS WELL AS BEING CONSIDERED A VALID BUSINESS DAY GET MARKED AS MISSING
*/
calcMissing
AS
(
    SELECT r.*
        ,CASE WHEN b.Date IS NULL AND lbd.BusinessDay IS NOT NULL THEN 1 ELSE 0 END AS Missing
    FROM reportRange r
        left join @dataSet b on r.DateStamp = b.Date and r.LocationCode = b.Location
        left join @locationsBusinessDays lbd on lbd.LocationCode = r.LocationCode AND r.DayOfWeekNumber = lbd.BusinessDay
)

--GROUP AS NECESSARY AND SUM THE MISSING DAYS
SELECT 
    MONTH(DateStamp) AS [Month]
    ,YEAR(DateStamp) AS [Year]
    ,LocationCode 
    ,SUM(Missing) as MissingDays
FROM calcMissing
GROUP BY 
    MONTH(DateStamp)
    ,YEAR(DateStamp)
    ,LocationCode
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...