计算给定日期的月份的总工作天数和经过的工作天数

时间:2018-11-09 23:51:00

标签: sql sql-server sql-server-2008 datetime

我陷入了SQL查询中。

我正在尝试在同一查询中计算两个不同的事物:

  1. 一个月中的工作日数(不包括周末)。
  2. 一个月过去了几天的工作日。

让我们说11月(截至2018年11月9日)

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 :(得分:1)

由于您要在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 (

答案 1 :(得分:0)

您可以尝试使用cte递归创建日历表,然后使用条件聚合函数获取结果。

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

答案 2 :(得分:0)

只要您需要一个月的数据,就可以在没有递归CTE的情况下执行此操作。

  • 每月头28天有20个工作日。只需检查29、30和31
  • 前7天有5个工作日,14天中有10个,依此类推。只需检查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