使用TSQL识别所有假日日期

时间:2018-01-22 22:29:23

标签: sql-server tsql

我做了很多寻找一个简单的解决方案,以便按年动态识别美国联邦假期。我无法找到更多关于棘手假期的信息。新年或独立日等节日很容易编程,因为它们是静态的。但是,有些更难以通过编程方式识别,例如总统日(2月的第3个星期一)或感恩节(11月的第4个星期四)。

2 个答案:

答案 0 :(得分:0)

这是我提出的解决方案。

我创建了一个表变量来存储整年的日期:

DECLARE @DateTable TABLE
(
    dtDate DATE,
    dtMonth VARCHAR(10),
    dtDayName VARCHAR(10),
    dtDayRank INT
);

填充@DateTable的前3列:

DECLARE @Year CHAR(4), @CurrentDate DATE
SET @Year = '2018'
SET @CurrentDate = CAST(@Year + '0101' AS DATE)

WHILE @CurrentDate <= CAST(@Year + '1231' AS DATE)
BEGIN
    INSERT INTO @DateTable (dtDate, dtMonth, dtDayName)
        VALUES (@CurrentDate, DATENAME(mm, @CurrentDate), DATENAME(dw, @CurrentDate))
    SET @CurrentDate = DATEADD(dd, 1, @CurrentDate)
END;

一旦我填充了表格,我就对行进行了排名并更新了表格:

UPDATE @DateTable
SET dtDayRank = rankdates.DayRank
FROM @DateTable datatable
    INNER JOIN (
        SELECT
            dtDate,
            DayRank = RANK() OVER (PARTITION BY dtMonth, dtDayName ORDER BY dtDate)  -- rank each DayOfWeek in order
        FROM @DateTable
    ) rankdates ON datatable.dtDate = rankdates.dtDate;

@DateTable的样本输出

Sample output from @DataTable

一旦我填充了@DateTable,我就可以使用逻辑来识别特定日期。

    SELECT 
        HolidayName = 'Presidents'' Day',
        ObservedDayOfWeek = dtDayName,
        HolidayObservedDate = dtDate
    FROM @DateTable
    WHERE dtMonth = 'February'
    AND dtDayName = 'Monday'
    AND dtDayRank = 3 

    SELECT 
        HolidayName = 'Thanksgiving Day',
        ObservedDayOfWeek = dtDayName,
        HolidayObservedDate = dtDate
    FROM @DateTable
    WHERE dtMonth = 'November'
    AND dtDayName = 'Thursday'
    AND dtDayRank = 4

输出

Sample output 2

我喜欢这个解决方案是如何解决的,因为我可以通过使用等于月份,星期几以及本月这一天发生的次数的谓词来识别一年中的任何日期。我把它变成了一个存储过程和表值函数,这样我就可以通过传递一年来运行它,然后返回那一年的所有假期。

这是一个很好的解决方案......有更简单的方法吗?

答案 1 :(得分:0)

您可以使用一些数学运算来简化您的SQL生活,例如2月的第3个星期一数学上必须在15和21之间(最早的第3个星期一有14天;最近的第3个星期一可以不超过20天)。如果您有tally table,则很容易找到所有日期。以下是总统当天如何做到这一点

with t1 as 
(SELECT 1 num
   FROM  (VALUES (1),(1),(1),(1),(1),(1),(1),(1)) subTable(n)
),
TallyTable as
(
  SELECT TOP 10000 ROW_NUMBER() OVER (ORDER BY (SELECT 1)) n
     FROM t1 a
        CROSS JOIN t1 b
        CROSS JOIN t1 c
        CROSS JOIN t1 d
        CROSS JOIN t1 e
        CROSS JOIN t1 f
),
DateTable as
(
  SELECT DateAdd(day,n,'1/1/2018') DateValue
  FROM TallyTable
)
SELECT *
  FROM DateTable DT
  WHERE DatePart(month,DT.DateValue) = 2 --February
    AND DatePart(dw,DT.DateValue) = 2 --Monday
    AND DatePart(day,DT.DateValue) BETWEEN 15 AND 21; --Day is between 15 and 21