1

例如:

@dtBegin = '2012-06-29' 

@input = 20

我希望输出为'2012-07-27'.

4

5 回答 5

1

我不得不在我的项目中解决同样的问题。Gordon Lionoff 的解决方案让我走上了正确的道路,但并不总是产生正确的结果。我还必须考虑从周末开始的日期。即在星期六或星期日增加 1 个工作日应该是星期一。这是处理工作日计算的最常用方法。

我基于 Gordon Linoff 的函数和Patrick McDonald 的优秀 C# 等价物创建了自己的解决方案

注意:我的解决方案仅在 DATEFIRST 设置为默认值 7 时才有效。如果您使用不同的 DATEFIRST 值,则必须更改1,7(1,7,8,9,10)位。

我的解决方案包含两个功能。处理边缘情况的“外部”函数和执行实际计算的“内部”函数。这两个函数都是表值函数,因此它们实际上将扩展为实现查询并通过查询优化器提供。

CREATE FUNCTION [dbo].[UTL_DateAddWorkingDays]
(   
    @date datetime,
    @days int
)
RETURNS TABLE AS RETURN 
(
    SELECT 
        CASE 
            WHEN @days = 0 THEN @date
            WHEN DATEPART(dw, @date) = 1 THEN (SELECT Date FROM [dbo].[UTL_DateAddWorkingDays_Inner](DATEADD(d, 1, @date), @days - 1))
            WHEN DATEPART(dw, @date) = 7 THEN (SELECT Date FROM [dbo].[UTL_DateAddWorkingDays_Inner](DATEADD(d, 2, @date), @days - 1))
            ELSE (SELECT Date FROM [dbo].[UTL_DateAddWorkingDays_Inner](@date, @days))
        END AS Date
)

如您所见,“外部”函数处理:

  • 不添加天数时,返回原始日期。这将保持周六和周日的日期不变。
  • 在星期天加上天数时,从星期一开始计数。这需要 1 天。
  • 在星期六加上天数时,从星期一开始数。这需要 1 天。
  • 在所有其他情况下,执行通常的计算

_

CREATE FUNCTION [dbo].[UTL_DateAddWorkingDays_Inner]
(   
    @date datetime,
    @days int
)
RETURNS TABLE AS RETURN 
(
    SELECT 
        DATEADD(d
        , (@days / 5) * 7 
          + (@days % 5) 
          + (CASE WHEN ((@days%5) + DATEPART(dw, @date)) IN (1,7,8,9,10) THEN 2 ELSE 0 END)
        , @date) AS Date
)

“内部”函数类似于 Gordon Linoff 的解决方案,不同之处在于它考虑了跨越周末边界但不跨越整周边界的日期。

最后,我创建了一个测试脚本来测试我的功能。预期值是使用Patrick McDonald 出色的 C# 等价物生成的,我用这个流行的计算器随机交叉引用了这些数据。

于 2012-09-20T15:23:26.013 回答
0

您可以在不借助日历表或用户定义函数的情况下执行此操作:

dateadd(d,
        (@input / 5) * 7 + -- get complete weeks out of the way
        mod(@input, 5) + -- extra days
        (case when ((@input%5) + datepart(dw, @dtbegin)%7) in (7, 1, 8) or
                   ((@input%5) + datepart(dw, @dtbegin)%7) < (@input%5)
              then 2
              else 0
         end),
        @dtbegin
       )

我不是说这很漂亮。但有时算术比连接或循环更可取。

于 2012-07-11T15:28:29.643 回答
0

这是我尝试过的:

CREATE function [dbo].[DateAddWorkDay]
(@days int,@FromDate Date)
returns Date
as
begin
declare @result date
set @result = (
select b
from
(
    SELECT
    b,
       (DATEDIFF(dd, a, b))
      -(DATEDIFF(wk, a, b) * 2)
      -(CASE WHEN DATENAME(dw, a) = 'Sunday' THEN 1 ELSE 0 END)
      -(CASE WHEN DATENAME(dw, b) = 'Saturday' THEN 1 ELSE 0 END)
      -COUNT(o.Holiday_Date) 
      as workday
    from
    (
    select 
    @FromDate as a,
    dateadd(DAY,num +@days,@FromDate) as b
    from (select row_number() over (order by (select NULL)) as num
          from Information_Schema.columns
         ) t
    where num <= 100 
    ) dt
    left join Holiday o on o.Holiday_Date between a and b and DATENAME(dw, o.Holiday_Date) not in('Saturday','Sunday') 
    where DATENAME(dw, b) not in('Saturday','Sunday')
          and b not in (select Holiday_Date from OP_Holiday where Holiday_Date between a and b) 

    group by a,b
) du
where workday =@days 

)
return @result 
end

哪里Holiday有一张桌子可以holiday_date作为假期的参考。

于 2014-06-28T02:51:01.123 回答
0

您可以使用下面提到的代码排除周末

go
if object_id('UTL_DateAddWorkingDays') is not null
drop function UTL_DateAddWorkingDays
go
create FUNCTION [dbo].[UTL_DateAddWorkingDays]
(   
    @date datetime,
    @daysToAdd int
)
RETURNS date
as
begin
declare @daysToAddCnt int=0,
@Dt datetime
declare @OutTable table
(
    id int identity(1,1),
    WeekDate date,
    DayId int
)
while @daysToAdd>0
begin
set @Dt=dateadd(day,@daysToAddCnt,@date)
--select @daysToAddCnt cnt,@Dt date,DATEPART(weekday,@Dt) dayId,@daysToAdd daystoAdd
    if(DATEPART(weekday,@Dt) <>7 and DATEPART(weekday,@Dt)<>1)
    begin
    insert into @outTable (WeekDate,DayId)
    select @Dt,DATEPART(weekday,DATEADD(day,@daysToAddCnt,@Dt))
    set @daysToAdd=@daysToAdd-1
    end
    set @daysToAddCnt=@daysToAddCnt+1

end
select @Dt=max(WeekDate) from @outTable
return @Dt
end
于 2021-09-16T12:05:57.933 回答
0

我意识到我参加这个派对大约晚了 8 年......但我接受了马丁的回答并将其更新为:

1. 单个标量函数而不是 2 个嵌套的表值函数

我使用他的原始测试脚本进行了测试,我的功能也通过了。此外,似乎重建为标量函数对性能有轻微的积极影响。两个版本似乎都从“缓冲区缓存”中受益,标量版本的非缓存性能提高了 25%,缓存提高了 40%。免责声明:我只是运行了两个版本并记录了时间,我没有进行任何体面的性能测试。

2. 包括对 DATEFIRST 的支持是星期一、星期六或星期日

我觉得 UDF 应该与datefirst设置无关。我在欧洲,星期一是这里的默认设置。如果不进行调整,原始功能将无法工作。

根据维基百科的说法,周一、周六和周日是一周中唯一真实的第一天。可以很容易地添加对其他的支持,但会使代码更加庞大,而且我很难想象一个真实的用例。

CREATE FUNCTION dbo.fn_addWorkDays
(   
    @date datetime,
    @days int
)
RETURNS DATETIME 
AS
BEGIN
    
    IF NOT @@DATEFIRST IN (1,6,7) BEGIN --UNSUPPORTED DATE FIRST
        RETURN NULL
    /* MONDAY = FRIST DAY */
    END ELSE IF @days = 0 BEGIN 
        RETURN @date
    END ELSE IF @@DATEFIRST = 1 AND DATEPART(dw, @date) = 7 BEGIN --SUNDAY
        SET @date = DATEADD(d, 1, @date)
        SET @days = @days - 1
    END ELSE IF @@DATEFIRST = 1 AND DATEPART(dw, @date) = 6 BEGIN --SATURDAY
        SET @date = DATEADD(d, 2, @date)
        SET @days = @days - 1

    /* SATURDAY = FRIST DAY */
    END ELSE IF @@DATEFIRST = 7 AND DATEPART(dw, @date) = 2 BEGIN --SUNDAY
        SET @date = DATEADD(d, 1, @date)
        SET @days = @days - 1
    END ELSE IF @@DATEFIRST = 7 AND DATEPART(dw, @date) = 1 BEGIN --SATURDAY
        SET @date = DATEADD(d, 2, @date)
        SET @days = @days - 1

    /* SUNDAY = FRIST DAY */
    END ELSE IF @@DATEFIRST = 7 AND DATEPART(dw, @date) = 1 BEGIN --SUNDAY
        SET @date = DATEADD(d, 1, @date)
        SET @days = @days - 1
    END ELSE IF @@DATEFIRST = 7 AND DATEPART(dw, @date) = 7 BEGIN --SATURDAY
        SET @date = DATEADD(d, 2, @date)
        SET @days = @days - 1

    END 

    DECLARE @return AS dateTime
    
    SELECT @return  =
        DATEADD(d
        , (@days / 5) * 7
          + (@days % 5)
          + (CASE 
                /* MONDAY = FRIST DAY */
                WHEN @@DATEFIRST = 1 AND ((@days%5) + DATEPART(dw, @date)) IN (6,7,8,9) THEN 2 
                /* SATURDAY = FRIST DAY */
                WHEN @@DATEFIRST = 7 AND ((@days%5) + DATEPART(dw, @date)) IN (1,2,8,9,10) THEN 2 
                /* SUNDAY = FRIST DAY */
                WHEN @@DATEFIRST = 7 AND ((@days%5) + DATEPART(dw, @date)) IN (1,7,8,9,10,11) THEN 2 
                ELSE 0 
            END)
        , @date) 

    RETURN @return 
END

我希望这可能会使某人受益!

于 2021-01-24T16:43:08.923 回答