SQL填充每月总工作日减去当前财政年度的银行假日

时间:2012-02-07 16:48:09

标签: sql sql-server sql-server-2008 date reporting-services

我正在查看一个看起来像我的第一张附图,但右侧列填充而非空白。逻辑如下:

数据必须是当前财务期。 4月将是2011年,3月将是2012年等等。

单个月的可用天数计算如下:

该特定财政年度的工作日总数(周一至周五)减去该特定月份的任何银行假日(我们保存在表格中 - 见第二张图片)。

假日表的列名从左到右: holidaytypeid 名称 holstart holend 。 表名: holidaytable

要计算累计月数'可用天数,这将是对单个月已填充数据进行求和的情况。例如,4月至5月的数据将是4月和5月的数据,以此类推。依此类推。

我需要完美格式的SQL查询,以便可以直接粘贴并运行(即使用正确的列名和表名)

感谢您的光临。

enter image description here

enter image description here

2 个答案:

答案 0 :(得分:6)

DECLARE @StartDate DATETIME, @EndDate DATETIME

SELECT  @StartDate = '01/04/2011',
        @EndDate = '31/03/2012'

CREATE TABLE #Data (FirstDay DATETIME NOT NULL PRIMARY KEY, WorkingDays INT NOT NULL)

;WITH DaysCTE ([Date]) AS
(   SELECT  @StartDate
    UNION ALL
    SELECT  DATEADD(DAY, 1, [Date])
    FROM    DaysCTE
    WHERE   [Date] <= @Enddate
)

INSERT INTO #Data
SELECT  MIN([Date]),
        COUNT(*) [Day]
FROM    DaysCTE
        LEFT JOIN HolidayTable
            ON [Date] BETWEEN HolStart AND HolEnd
WHERE   HolidayTypeID IS NULL
AND     DATENAME(WEEKDAY, [Date]) NOT IN ('Saturday', 'Sunday')
GROUP BY DATEPART(MONTH, [Date]), DATEPART(YEAR, [Date])
OPTION (MAXRECURSION 366)

DECLARE @Date DATETIME
SET @Date = (SELECT MIN(FirstDay) FROM #Data)

SELECT  Period,
        WorkingDays [Days Available (Minus the Holidays)]
FROM    (   SELECT  DATENAME(MONTH, Firstday) [Period],
                    WorkingDays,
                    0 [SortField],
                    FirstDay
            FROM    #Data
            UNION
            SELECT  DATENAME(MONTH, @Date) + ' - ' + DATENAME(MONTH, Firstday),
                    (   SELECT  SUM(WorkingDays)
                        FROM    #Data b
                        WHERE   b.FirstDay <= a.FirstDay
                    ) [WorkingDays],
                    1 [SortField],
                    FirstDay 
            FROM    #Data a
            WHERE   FirstDay > @Date
        ) data
ORDER BY SortField, FirstDay

DROP TABLE #Data

如果您执行此操作超过1年,则需要更改以下行:

OPTION (MAXRECURSION 366)

否则您将收到错误 - 该号码需要高于您查询的天数。


修改

我刚接触到我的这个旧答案并且真的不喜欢它,有很多事情我现在认为是不好的做法,所以我要纠正所有问题:

  1. I did not terminate statements with a semi colon properly
  2. 使用递归CTE生成日期列表
  3. Did not include the column list for an insert
  4. 使用DATENAME来消除特定语言的周末,​​更好地明确设置DATEFIRST并使用DATEPART
  5. 使用LEFT JOIN/IS NULL代替NOT EXISTS来取消假日表中的记录。 In SQL Server LEFT JOIN/IS NULL is less efficient than NOT EXISTS
  6. 这些都是微不足道的事情,但是在审查别人的查询时,我会批评这些事情(至少在我的脑海里,如果不是在外面),那么我的工作真的不能纠正!重写查询会给。

    SET DATEFIRST 1;
    
    DECLARE @StartDate DATETIME = '20110401',
            @EndDate DATETIME = '20120331';
    
    CREATE TABLE #Data (FirstDay DATETIME NOT NULL PRIMARY KEY, WorkingDays INT NOT NULL);
    
    WITH DaysCTE ([Date]) AS
    (   SELECT  TOP (DATEDIFF(DAY, @StartDate, @EndDate) + 1)
                DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY a.object_id) - 1, @StartDate)
        FROM    sys.all_objects a
    )
    INSERT INTO #Data (FirstDay, WorkingDays)
    SELECT  FirstDay =  MIN([Date]),
            WorkingDays = COUNT(*) 
    FROM    DaysCTE d
    WHERE   DATEPART(WEEKDAY, [Date]) NOT IN (6, 7)
    AND     NOT EXISTS
            (   SELECT  1
                FROM    dbo.HolidayTable ht
                WHERE   d.[Date] BETWEEN ht.HolStart AND ht.HolEnd
            )
    GROUP BY DATEPART(MONTH, [Date]), DATEPART(YEAR, [Date]);
    
    DECLARE @Date DATETIME = (SELECT MIN(FirstDay) FROM #Data);
    
    SELECT  Period,
            [Days Available (Minus the Holidays)] = WorkingDays 
    FROM    (   SELECT  DATENAME(MONTH, Firstday) [Period],
                        WorkingDays,
                        0 [SortField],
                        FirstDay
                FROM    #Data
                UNION
                SELECT  DATENAME(MONTH, @Date) + ' - ' + DATENAME(MONTH, Firstday),
                        (   SELECT  SUM(WorkingDays)
                            FROM    #Data b
                            WHERE   b.FirstDay <= a.FirstDay
                        ) [WorkingDays],
                        1 [SortField],
                        FirstDay 
                FROM    #Data a
                WHERE   FirstDay > @Date
            ) data
    ORDER BY SortField, FirstDay;
    
    DROP TABLE #Data;
    

    最后一点,这个查询变得更加简单,calendar table存储了所有日期,并且有工作日,假期等标志,而不是使用只存储假期的假日表。

答案 1 :(得分:1)

让我在这篇文章中加几美分。刚刚完成计算计算小时数和实际小时数之间的差异。下面的代码转换为函数。到目前为止,逻辑没有问题:

declare @date datetime = '11/07/2012'
declare @t table (HolidayID int IDENTITY(1,1) primary key,
    HolidayYear int,
    HolidayName varchar(50),
    HolidayDate datetime)

INSERT @t
VALUES(2012, 'New Years Day', '01/02/2012'),
(2012,'Martin Luther King Day', '01/16/2012'),
(2012,'Presidents Day', '02/20/2012'),
(2012,'Memorial Day', '05/28/2012'),
(2012,'Independence Day', '07/04/2012'),
(2012,'Labor Day', '09/03/2012'),
(2012,'Thanksgiving Day', '11/22/2012'),
(2012,'Day After Thanksgiving', '11/23/2012'),
(2012,'Christmas Eve', '12/24/2012'),
(2012,'Christmas Day', '12/25/2012'),
(2013, 'New Years Day', '01/01/2013'),
(2013,'Martin Luther King Day', '01/21/2013'),
(2013,'Presidents Day', '02/18/2013'),
(2013,'Good Friday', '03/29/2013'),
(2013,'Memorial Day', '05/27/2013'),
(2013,'Independence Day', '07/04/2013'),
(2013,'Day After Independence Day', '07/05/2013'),
(2013,'Labor Day', '09/02/2013'),
(2013,'Thanksgiving Day', '11/28/2013'),
(2013,'Day After Thanksgiving', '11/29/2013'),
(2013,'Christmas Eve', NULL),
(2013,'Christmas Day', '12/25/2013')


DECLARE @START_DATE DATETIME, 
    @END_DATE DATETIME, 
    @Days int

SELECT  @START_DATE = DATEADD(MONTH, DATEDIFF(MONTH, 0, @date), 0) 

SELECT  @END_DATE = DATEADD(month, 1,@START_DATE)

;WITH CTE AS
(
    SELECT DATEADD(DAY, number, (DATEADD(MONTH, DATEDIFF(MONTH, 0, @date), 0) )) CDate
    FROM master.dbo.spt_values where type = 'p' and number between 0 and 365
        EXCEPT
    SELECT HolidayDate FROM @t WHERE HolidayYear = YEAR(@START_DATE) 
)
SELECT @Days = COUNT(CDate) --, datepart(dw, CDate) WDay
FROM CTE 
WHERE (CDate >=@START_DATE and CDate < @END_DATE) AND DATEPART(dw, CDate) NOT IN(1,7)

SELECT @Days