在SQL中添加工作日,无需循环

Mat*_*ing 33 sql date

我目前在我的SQL数据库中有一个函数,它在一个日期中添加了一定的工作日,例如,如果您输入的是星期四的日期并添加两天,它将返回下一个星期一的日期.我对任何假期都不感到烦恼,只有周末才被排除在外.

问题是,这当前使用while循环完成,并且它似乎大大减慢了在生成表时使用它的存储过程.有没有人知道是否有任何方法可以在没有while循环或游标的情况下执行此计算?

仅供参考,这是当前的功能:

ALTER FUNCTION [dbo].[AddWorkDaysToDate]
(   
@fromDate       datetime,
@daysToAdd      int
)
RETURNS datetime
AS
BEGIN   
DECLARE @toDate datetime
DECLARE @daysAdded integer

-- add the days, ignoring weekends (i.e. add working days)
set @daysAdded = 1
set @toDate = @fromDate

while @daysAdded <= @daysToAdd
begin
    -- add a day to the to date
    set @toDate = DateAdd(day, 1, @toDate)
    -- only move on a day if we've hit a week day
    if (DatePart(dw, @toDate) != 1) and (DatePart(dw, @toDate) != 7)
    begin
        set @daysAdded = @daysAdded + 1
    end
end

RETURN @toDate

END
Run Code Online (Sandbox Code Playgroud)

小智 31

如果有人在寻找TSQL解决方案,那就更好了.没有循环,没有表格,没有案例陈述和负面作品.谁能打败那个?

CREATE FUNCTION[dbo].[AddBusinessDays](@Date date,@n INT)
RETURNS DATE AS 
BEGIN
DECLARE @d INT;SET @d=4-SIGN(@n)*(4-DATEPART(DW,@Date));
RETURN DATEADD(D,@n+((ABS(@n)+@d-2)/5)*2*SIGN(@n)-@d/7,@Date);
END
Run Code Online (Sandbox Code Playgroud)

  • 但是这个确实假设了一个特定的"DATEFIRST"设置(7),其他一些不需要. (2认同)
  • 如果传入的日期是星期日,则负值不起作用. (2认同)

Dam*_*ver 16

这个答案自被接受以来已经有了很大的改变,因为原来是错的.我对新查询更有信心,但它并不依赖于DATEFIRST


我认为这应该涵盖它:

declare @fromDate datetime
declare @daysToAdd int

select @fromDate = '20130123',@DaysToAdd = 4

declare @Saturday int
select @Saturday = DATEPART(weekday,'20130126')

;with Numbers as (
    select 0 as n union all select 1 union all select 2 union all select 3 union all select 4
), Split as (
    select @DaysToAdd%5 as PartialDays,@DaysToAdd/5 as WeeksToAdd
), WeekendCheck as (
    select WeeksToAdd,PartialDays,MAX(CASE WHEN DATEPART(weekday,DATEADD(day,n.n,@fromDate))=@Saturday THEN 1 ELSE 0 END) as HitWeekend
    from
    Split t
        left join
    Numbers n
        on
            t.PartialDays >= n.n
group by WeeksToAdd,PartialDays
)
select DATEADD(day,WeeksToAdd*7+PartialDays+CASE WHEN HitWeekend=1 THEN 2 ELSE 0 END,@fromDate)
from WeekendCheck
Run Code Online (Sandbox Code Playgroud)

我们将时间分成若干周和一周内的几天.然后,我们使用一个小数字表来计算,如果添加这几天将导致我们打一个星期六.如果确实如此,那么我们需要再增加2天.

  • 非常好,效果很好.对于那些使用可能将其datefirst设置为7的英国SQL数据库的人,即星期日,只记得将WHEN 6更改为WHEN 7,将WHEN 7更改为WHEN 8. (2认同)

Nat*_*ook 9

根据此问题接受的答案,以下用户定义函数(UDF)应该适用于所有情况 - 无论设置如何@@DateFirst.

更新:如下面的评论所示,此功能是为FromDate设计的工作日.当一个周末日作为FromDate传入时,行为是未定义的.

ALTER FUNCTION [dbo].[BusinessDaysDateAdd] 
(
   @FromDate datetime,
   @DaysToAdd int
)
RETURNS datetime
AS
BEGIN
   DECLARE @Result datetime

   SET @Result = DATEADD(day, (@DaysToAdd % 5) + CASE ((@@DATEFIRST + DATEPART(weekday, @FromDate) + (@DaysToAdd % 5)) % 7)
                                                 WHEN 0 THEN 2
                                                 WHEN 1 THEN 1
                                                 ELSE 0 END, DATEADD(week, (@DaysToAdd / 5), @FromDate))

   RETURN @Result
END
Run Code Online (Sandbox Code Playgroud)

  • 请注意,但我希望`BusinessDaysDateAdd('2013-09-01',5)`返回2013-09-06,但它会返回2013-09-09. (4认同)
  • 将5个工作日添加到星期日发生的日期(例如"2014-04-27")应返回以下星期五,但此函数将返回以下星期一(添加7天而不是5天). (2认同)

Jua*_*gui 8

这个答案基于@ ElmerMiller的答案.

它修复了来自@FistOfFury的星期日评论的负值

如果传入的日期是星期日,则负值不起作用

来自@Damien_The_Unbeliever的DATEFIRST设置评论

但是这个确实假定了一个特定的DATEFIRST设置(7),其他一些不需要.

现在纠正的功能

CREATE FUNCTION[dbo].[AddBusinessDays](@Date DATE,@n INT)
RETURNS DATE AS 
BEGIN
DECLARE @d INT,@f INT,@DW INT;
SET @f=CAST(abs(1^SIGN(DATEPART(DW, @Date)-(7-@@DATEFIRST))) AS BIT)
SET @DW=DATEPART(DW,@Date)-(7-@@DATEFIRST)*(@f^1)+@@DATEFIRST*(@f&1)
SET @d=4-SIGN(@n)*(4-@DW);
RETURN DATEADD(D,@n+((ABS(@n)+(@d%(8+SIGN(@n)))-2)/5)*2*SIGN(@n)-@d/7,@Date);
END
Run Code Online (Sandbox Code Playgroud)

  • 这个对我有用!但是,我不确定它是如何工作的。你能解释一下代码吗? (4认同)

ble*_*eah 5

您是否考虑过预先填充包含所有工作日(使用您的函数)的查找表,例如WorkingDays(int DaySequenceId,Date WorkingDate),然后您可以通过选择@fromDate的DaySequenceId来使用此表并添加@daysToAdd以获取新的工作日期.显然,此方法还有管理WorkingDays表的额外开销,但您可以使用您期望的日期范围预先填充它.另一个缺点是可以计算的工作日期只是WorkingDays表中包含的工作日期.


小智 5

*我知道这是一个旧线程,但是不久前发现了一些非常有用的东西,对其进行了修改并得到了这个。

select ((DATEADD(d,DATEDIFF(d,0,(DATEADD (d,2,@fromDate))),@numbOfDays)))*
Run Code Online (Sandbox Code Playgroud)

更新:很抱歉,我急于在单个语句中找到一段代码,并且为了避免使用函数,我在这里张贴了不正确的代码。

如果要添加的天数为7天或更少,则可以使用上述位。

为了更好的理解,我更改了带有必需参数的代码。

无论如何,我最终使用了上面提到的“内特·库克”。并将其用作一行代码。(因为我限制使用函数)

内特的密码

select(
DATEADD(day, (@days % 5) + 
CASE ((@@DATEFIRST + DATEPART(weekday, GETDATE()) + (@days % 5)) % 7)
WHEN 0 THEN 2
WHEN 1 THEN 1
ELSE 0 END, DATEADD(week, (@days / 5), GETDATE()))
)
Run Code Online (Sandbox Code Playgroud)


Fis*_*ury 5

为了扩展上面 Amine 的评论和 Nate Cook 的回答,对此的简单解决方案是:

declare @DaysToAdd int , @FromDate datetime 
set @DaysToAdd=-5      --5 days prior is 3/28/14
set @FromDate='4/4/14'
select 
    DATEADD(day, (@DaysToAdd % 5) 
    +   CASE 
        WHEN ((@@DATEFIRST + DATEPART(weekday, @FromDate)) % 7 + (@DaysToAdd % 5)) > 6 THEN 2 
        ELSE 0 
        END
    , DATEADD(week, (@DaysToAdd / 5), @FromDate))
Run Code Online (Sandbox Code Playgroud)

请注意,您可以添加或减去天数以分别在时间上前进和后退。