我做了很多寻找一个简单的解决方案,以便按年动态识别美国联邦假期。我无法找到更多关于棘手假期的信息。新年或独立日等节日很容易编程,因为它们是静态的。但是,有些更难以通过编程方式识别,例如总统日(2月的第3个星期一)或感恩节(11月的第4个星期四)。
答案 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的样本输出
一旦我填充了@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
输出
我喜欢这个解决方案是如何解决的,因为我可以通过使用等于月份,星期几以及本月这一天发生的次数的谓词来识别一年中的任何日期。我把它变成了一个存储过程和表值函数,这样我就可以通过传递一年来运行它,然后返回那一年的所有假期。
这是一个很好的解决方案......有更简单的方法吗?
答案 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