Рассчитать общее и прошедшее количество рабочих дней в месяце на данную дату - PullRequest
0 голосов
/ 10 ноября 2018

Я застрял с запросом SQL.

Я пытаюсь вычислить две разные вещи в одном запросе:

  1. Количество рабочих дней в месяце (исключая выходные).
  2. Сколько рабочих дней прошло в месяце.

Допустим, за ноябрь (по состоянию на 9.09.2008)

no.of business days        no. of business days passed
22                                7

Я пытался так:

WITH cteAllDates AS 
(
    DECLARE @StartDate DATETIME
    DECLARE @EndDate DATETIME

    SET @StartDate = '10/01/2018'
    SET @EndDate = '10/31/2018'

    SELECT
        (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 x
) AS y
SELECT x
FROM cteAllDates

Я хотел бы создать виртуальную таблицу, чтобы я мог использовать эти поля в своем полном запросе. И если это возможно, я могу сделать это с GETDATE() и не объявлять даты каждый раз.

Ответы [ 3 ]

0 голосов
/ 10 ноября 2018

Так как вы хотите сделать это в CTE и на основе более раннего ответа, который вы нашли в SO, вот версия, которая делает все это за текущий месяц без необходимости определять даты начала и окончания:

РЕДАКТИРОВАТЬ: Чтобы создать праздничный стол

Сначала создайте праздничный стол. Не делайте это каждый раз, делайте это постоянным столом и сохраняйте его заполненным всеми необходимыми праздниками - Пасхой, Рождеством и т. Д.

create table holidays(holiday date)
insert holidays values ('2018-09-23'),('2018-09-24')

Теперь запрос, включая проверку количества праздников между датами

;with dates as(
    select dateadd(d,-day(getdate())+1,convert(date,getdate())) as startofmonth,
    dateadd(d,-1,dateadd(m,1,dateadd(d,-day(getdate())+1,convert(date,getdate())))) as endofmonth,
    convert(date,getdate()) as today
)
,holidaycount as (
    select count(*) as holidaysinmonth,
        sum(case when holiday<=today then 1 else 0 end) as holidaystodate
    from dates
    join holidays on holiday between startofmonth and endofmonth
)
,daycounts as(
    select dates.*,

       (DATEDIFF(dd, startofmonth, endofmonth) + 1)
      -(DATEDIFF(wk, startofmonth, endofmonth) * 2)
      -(CASE WHEN DATENAME(dw, startofmonth) = 'Sunday' THEN 1 ELSE 0 END) 
      -(CASE WHEN DATENAME(dw, endofmonth) = 'Saturday' THEN 1 ELSE 0 END)
      -isnull(holidaysinmonth,0) as wkdaysinmonth,

       (DATEDIFF(dd, startofmonth, today) + 1)
      -(DATEDIFF(wk, startofmonth, today) * 2)
      -(CASE WHEN DATENAME(dw, startofmonth) = 'Sunday' THEN 1 ELSE 0 END) 
      -(CASE WHEN DATENAME(dw, today) = 'Saturday' THEN 1 ELSE 0 END)
      -isnull(holidaystodate,0) as wkdaystodate

    from dates
    cross join holidaycount
) 

select * from daycounts

РЕДАКТИРОВАТЬ: Если вы не можете создать временную таблицу, добавьте ее в качестве дополнительного CTE перед праздничным счетом, например, так:

,holidays as (
    select holiday from (values ('2018-11-23'),('2018-11-24')) t(holiday)
)
,holidaycount as (
0 голосов
/ 10 ноября 2018

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

  • В первых 28 днях месяца 20 рабочих дней. Просто отметьте 29, 30 и 31
  • В первые 7 дней 5 рабочих дней, 10 в 14 и т. Д. Просто проверьте 6 дней.
DECLARE @dt AS DATE = '2018-11-09';
WITH vars1 AS (
    SELECT
        d1 = DATEADD(dd, 1, EOMONTH(@dt, -1)),
        dn = EOMONTH(@dt),
        wks = DAY(@dt) / 7
), vars AS (
    SELECT
        d1, -- first day of month
        dn, -- last day of month
        wks, -- number of 7-day intervals elapsed
        d29 = DATEADD(dd, 28, d1), -- 29th day of month
        d30 = DATEADD(dd, 29, d1),
        d31 = DATEADD(dd, 30, d1),
        dp1 = DATEADD(dd, wks * 7 + 0, d1), -- wks * 7 gives you 0, 7, 14, 21 or 28
        dp2 = DATEADD(dd, wks * 7 + 1, d1), -- wks * 7 + 0 ... 5 are the dates to check
        dp3 = DATEADD(dd, wks * 7 + 2, d1),
        dp4 = DATEADD(dd, wks * 7 + 3, d1),
        dp5 = DATEADD(dd, wks * 7 + 4, d1),
        dp6 = DATEADD(dd, wks * 7 + 5, d1)
    FROM vars1
)
SELECT 
    [no. of business days] = 20 + 
    IIF(d29 <= dn AND DATENAME(dw, d29) NOT IN ('Saturday', 'Sunday'), 1, 0) + 
    IIF(d30 <= dn AND DATENAME(dw, d30) NOT IN ('Saturday', 'Sunday'), 1, 0) + 
    IIF(d31 <= dn AND DATENAME(dw, d31) NOT IN ('Saturday', 'Sunday'), 1, 0),
    [no. of business days passed] = wks * 5 +
    IIF(dp1 <= @dt AND DATENAME(dw, dp1) NOT IN ('Saturday', 'Sunday'), 1, 0) + 
    IIF(dp2 <= @dt AND DATENAME(dw, dp2) NOT IN ('Saturday', 'Sunday'), 1, 0) + 
    IIF(dp3 <= @dt AND DATENAME(dw, dp3) NOT IN ('Saturday', 'Sunday'), 1, 0) + 
    IIF(dp4 <= @dt AND DATENAME(dw, dp4) NOT IN ('Saturday', 'Sunday'), 1, 0) + 
    IIF(dp5 <= @dt AND DATENAME(dw, dp5) NOT IN ('Saturday', 'Sunday'), 1, 0) + 
    IIF(dp6 <= @dt AND DATENAME(dw, dp6) NOT IN ('Saturday', 'Sunday'), 1, 0)
FROM vars

DB Fiddle

0 голосов
/ 10 ноября 2018

Вы можете попробовать использовать cte recursive make в календарной таблице, а затем использовать функцию агрегатирования условий, чтобы получить свой результат.

DECLARE @StartDate DATETIME
DECLARE @EndDate DATETIME
SET @StartDate = DATEADD(DAY, 1, EOMONTH(GETDATE(), -1));
SET @EndDate = DATEADD(DAY, 1, EOMONTH(GETDATE()));

;WITH CTE AS (
    select @StartDate startdt,@EndDate enddt
    UNION ALL
    SELECT  DATEADD (day ,1 , startdt) , @EndDate
    FROM CTE
    WHERE DATEADD (day,1,startdt) <= @EndDate
)


select SUM(CASE WHEN DATENAME(dw, startdt) NOT IN ('Sunday','Saturday') THEN 1 END) 'no.of business days',
       SUM(CASE WHEN DATENAME(dw, startdt) NOT IN ('Sunday','Saturday') AND GETDATE() >= startdt THEN 1 END) 'no. of business days passed'
FROM CTE

sqlfiddle

Результат

no.of business days     no. of business days passed
22                      7
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...