使用假期查找表在存储过程中计算3个工作日

时间:2013-06-07 13:39:35

标签: sql sql-server-2008 tsql date

我有一个棘手的问题,我在精神层面上苦苦挣扎。

在我们的数据库中,我们有一个表格显示未来几年的英国假期,而存储的函数会将记录集返回给我的前端。

我的记录集中有一个名为'deletable'的标志,它允许前端决定是否可以在数据网格中显示上下文菜单,从而允许删除该记录。

目前,测试(在我的存储过程中)仅检查日期列是否具有三天或更早的日期。

case when DATEDIFF(d,a.[date],GETDATE()) > 3 then 1 else 0 end as [deletable]

如何通过检查周末和Holidays表'Holiday'列(这是一个Datetime)来修改它以查找上一个工作日期,并查看我的记录集行中的[date]列是否在3个工作日之前,考虑到假期表和周末的假期?

所以如果[date]列是5月23日,今天的日期是5月28日,那么该列返回0,因为27日是银行假日,而第二天它将返回1,因为会有3个以上工作日差异。

有没有一种优雅的方法可以做到这一点?

感谢 菲利普

3 个答案:

答案 0 :(得分:2)

好的,我完全重构了这个。

declare
    @DeletablePeriodStart datetime,
    @BusinessDays int

set @DeletablePeriodStart = dateadd(d,0,datediff(d,0,getdate()))
set @BusinessDays = 0

while @BusinessDays < 3
begin
    set @DeletablePeriodStart = dateadd(d,-1,@DeletablePeriodStart)
    if datepart(dw,@DeletablePeriodStart) not in (1,7) and
        not exists (select * from HolidayTable where Holiday = @DeletablePeriodStart)
    begin
        set @BusinessDays = @BusinessDays + 1
    end
end

这一次它没有做出任何假设。它运行一个快速循环检查每天是否是一个有效的工作日,并且直到它计算其中三个为止。然后稍后检查a.[date] >= @DeletablePeriodStart

答案 1 :(得分:1)

您应该从DATEDIFF中减去。[date]和GETDATE()之间的假期数。尝试这样的事情:

case when DATEDIFF(d,a.[date],GETDATE())-(
    SELECT COUNT(*) FROM Holidays 
    WHERE HolidayDate BETWEEN a.[date] AND GETDATE()
)>3 then 1 else 0 end as [deletable]

勒兹

答案 2 :(得分:1)

我假设您没有Calendar table,虽然我强烈建议您创建一个,但如果没有一个,您仍然可以实现这一目标:

以下内容将为您提供昨天倒退的2047个日期列表(使用系统表Master..spt_values):

WITH Dates AS
(   SELECT  Date = DATEADD(DAY, -number, CAST(GETDATE() AS DATE))
    FROM    Master..spt_values
    WHERE   type = 'P'
    AND     number > 0
)
SELECT  Dates.Date
FROM    Dates
ORDER BY Dates.Date DESC;

然后,您需要使用以下内容从表中排除周末和假期:

SET DATEFIRST 1;

WITH Dates AS
(   SELECT  Date = DATEADD(DAY, -number, CAST(GETDATE() AS DATE))
    FROM    Master..spt_values
    WHERE   type = 'P'
    AND     number > 0
)
SELECT  Dates.Date
FROM    Dates
WHERE   DATEPART(WEEKDAY, Dates.Date) <= 5
AND     NOT EXISTS
        (   SELECT  1
            FROM    HolidayTable h
            WHERE   Dates.Date = h.HolidayDate
        )
ORDER BY Dates.Date DESC;

N.B。您应该明确设置DATEFIRST而不是依赖服务器默认值

上面给出了今天之前的工作日列表,然后您可以使用ROW_NUMBER()函数,在列表中获得第3次出现,给出最终查询:

WITH Dates AS
(   SELECT  Date = DATEADD(DAY, -number, CAST(GETDATE() AS DATE))
    FROM    Master..spt_values
    WHERE   type = 'P'
    AND     number > 0
), WorkingDays AS
(   SELECT  Dates.Date, RN = ROW_NUMBER() OVER(ORDER BY Dates.Date DESC)
    FROM    Dates
    WHERE   DATEPART(WEEKDAY, Dates.Date) <= 5
    AND     NOT EXISTS
            (   SELECT  1
                FROM    HolidayTable h
                WHERE   Dates.Date = h.HolidayDate
            )
)
SELECT  WorkingDays.Date
FROM    WorkingDays
WHERE   RN = 3;

或者如果您愿意,可以使用一个查询(完全相同的原理)来完成:

SELECT  d.Date
FROM    (   SELECT  Date = DATEADD(DAY, -number, CAST(GETDATE() AS DATE)), RN = ROW_NUMBER() OVER(ORDER BY number)
            FROM    Master..spt_values
            WHERE   type = 'P'
            AND     number > 0
            AND     DATEPART(WEEKDAY, DATEADD(DAY, -number, CAST(GETDATE() AS DATE))) <= 5
            AND     NOT EXISTS
                    (   SELECT  1
                        FROM    HolidayTable h
                        WHERE   DATEADD(DAY, -number, CAST(GETDATE() AS DATE)) = h.HolidayDate
                    )
        ) d
WHERE   rn = 3;