2012-10-12 77 views
3

我曾見過大量有關如何創建SQL Server函數的帖子,該函數會將給定數量的工作日添加到日期。但是,他們中沒有一個完全按照我需要的方式計算。我們有一個目前在代碼中完成的功能,但我希望移動到本地SQL Server功能(供存儲過程和查詢使用)。在我們開始評估倉儲費用之前,我們給客戶5個工作日的時間來收集貨物。 5個工作日不包括週末和節假日(我們有一個包含假期的表格)。這裏的竅門是,無論是週末還是假期,我都需要在5個工作日後立即獲得日期。所以我需要函數返回上一個工作日期,而不是第一個工作日之後。因此,舉例來說:將工作日添加到日期的SQL Server函數

Oct 20th (Sat) plus 5 working days = Oct 26th (Fri) 
Oct 21st (Sun) plus 5 working days = Oct 26th (Fri) 
Oct 22nd (Mon) plus 5 working days = Oct 29th (Mon) 
May 19th (Sat) plus 5 working days with May 21st a holiday = May 28th 

的5個工作日內是當前分配,但這樣的工作日天數必須是一個參數,這可能在未來改變。此外,該功能可能會用於相當大的數據集,所以我寧願不用循環來做這件事。我們正在運行SQL Server 2008.

編輯:這不是「Add business days to date in SQL without loops」的重複,因爲他們希望結束日期爲工作日。我希望我的結束日期是緊隨最後一個寬限日之後的任何日期(例如:5個工作日週一至週五我希望返回週六日期,而不是下一個週一)。

+0

可能的重複[在SQL中添加無日期的工作日日期](http://stackoverflow.com/questions/5471524/add-business-days-to-date-in-sql-without-loops) – assylias

回答

2
create table holidays (
    date date); 
GO 

create function dbo.findWorkDayAfter(@date datetime, @days int) 
returns date as 
begin 
return (
    select thedate 
    from (
    select thedate=dateadd(d,v.day,cast(@date as date)), 
     rn=row_number() over (order by v.day) 
    from (values(1),(2),(3),(4),(5),(6),(7),(8),(9),(10))v(day) 
    left join holidays h on h.date = dateadd(d,v.day,cast(@date as date)) 
    where h.date is null and left(datename(dw,dateadd(d,v.day,cast(@date as date))),1) <> 'S' 
) x 
    where @days = rn 
) 
end 
GO 

除非你有很長的假期,10天應該足以找到第五個下一個工作日。如果你需要增加它。

如果您需要從一個日期開始的較大的工作日數,您可以使用這將滿足一年或三年。

alter function dbo.findWorkDayAfter(@date datetime, @days int) 
returns date as 
begin 
return (
    select thedate 
    from (
    select thedate=dateadd(d,v.number,cast(@date as date)), 
     rn=row_number() over (order by v.number) 
    from master..spt_values v 
    left join holidays h on h.date = dateadd(d,v.number,cast(@date as date)) 
    where h.date is null and left(datename(dw,dateadd(d,v.number,cast(@date as date))),1) <> 'S' 
    and v.number >= 1 and v.type='p' 
) x 
    where @days = rn 
) 
end 
GO 
+0

似乎沒有工作?插入10月20日的日期將返回10月25日而不是26日。同樣,插上第23位將返回第28位而不是第29位。 – Caynadian

+0

我必須在更新它之前抓住它。 23-> 30 btw – RichardTheKiwi

+0

現在效果很好,謝謝!我唯一的問題是如果工作日超過5天會發生什麼?我試着插入10天,而得到一個NULL結果。 – Caynadian

1

所有功勞都波格丹馬克西姆&彼得·莫滕森從Count work days between two dates。這是他們的帖子,我只是給函數添加了假期(假設你有一個帶有日期時間字段「HolDate」的表「tblHolidays」。) 對於新手來說,最後有一個測試腳本。 快樂編碼!

--Changing current database to the Master database allows function to be shared by everyone. 
USE MASTER 
GO 
--If the function already exists, drop it. 
IF EXISTS 
(
    SELECT * 
    FROM dbo.SYSOBJECTS 
    WHERE ID = OBJECT_ID(N'[dbo].[fn_WorkDays]') 
    AND XType IN (N'FN', N'IF', N'TF') 
) 

DROP FUNCTION [dbo].[fn_WorkDays] 
GO 
CREATE FUNCTION dbo.fn_WorkDays 
--Presets 
--Define the input parameters (OK if reversed by mistake). 
(
    @StartDate DATETIME, 
    @EndDate DATETIME = NULL [email protected] replaced by @StartDate when DEFAULTed 
) 

--Define the output data type. 
RETURNS INT 

AS 
--Calculate the RETURN of the function. 
BEGIN 
    --Declare local variables 
    --Temporarily holds @EndDate during date reversal. 
    DECLARE @Swap DATETIME 

    --If the Start Date is null, return a NULL and exit. 
    IF @StartDate IS NULL 
     RETURN NULL 

    --If the End Date is null, populate with Start Date value so will have two dates (required by DATEDIFF below). 
    IF @EndDate IS NULL 
     SELECT @EndDate = @StartDate 

    --Strip the time element from both dates (just to be safe) by converting to whole days and back to a date. 
    --Usually faster than CONVERT. 
    --0 is a date (01/01/1900 00:00:00.000) 
    SELECT @StartDate = DATEADD(dd,DATEDIFF(dd,0,@StartDate), 0), 
      @EndDate = DATEADD(dd,DATEDIFF(dd,0,@EndDate) , 0) 

    --If the inputs are in the wrong order, reverse them. 
    IF @StartDate > @EndDate 
     SELECT @Swap  = @EndDate, 
       @EndDate = @StartDate, 
       @StartDate = @Swap 

    --Calculate and return the number of workdays using the input parameters. 
    --This is the meat of the function. 
    --This is really just one formula with a couple of parts that are listed on separate lines for documentation purposes. 
    RETURN (
     SELECT 
     --Start with total number of days including weekends 
     (DATEDIFF(dd,@StartDate, @EndDate)+1) 
     --Subtact 2 days for each full weekend 
     -(DATEDIFF(wk,@StartDate, @EndDate)*2) 
     --If StartDate is a Sunday, Subtract 1 
     -(CASE WHEN DATENAME(dw, @StartDate) = 'Sunday' 
      THEN 1 
      ELSE 0 
     END) 
     --If EndDate is a Saturday, Subtract 1 
     -(CASE WHEN DATENAME(dw, @EndDate) = 'Saturday' 
      THEN 1 
      ELSE 0 
     END) 
     --Subtract all holidays 
     -(Select Count(*) from tblHolidays 
      where HolDate between @StartDate and @EndDate) 
     ) 
    END 
GO 

/* 
    -- Test Script 
declare @EndDate datetime= dateadd(m,2,getdate()) 
print @EndDate 
select [Master].[dbo].[fn_WorkDays] (getdate(), @EndDate) 
*/ 
1

我只好給定的日期類似的規定早這麼共享以下函數添加沒有給出天(僅限工作日)後返回新的日期,這也給選項,從週末排除週六。

ALTER FUNCTION [dbo].[AddDaysAndWeekends](
    @StartDate DATETIME, 
    @NoOfDays INT, 
    @IsSatrudayHoliday bit 
) 

RETURNS DATETIME AS BEGIN 
    while (@NoOfDays>0) 
    begin 
     --add 1 day 
     set @StartDate = DateAdd(day,1,@StartDate) 
     --skip weekends 
     while (DATEPART(dw, @StartDate) = 1 or (@IsSatrudayHoliday = 1 and DATEPART(dw, @StartDate) = 7)) 
     begin 
      set @StartDate = DateAdd(day,1,@StartDate) 
     end 
     set @NoOfDays = @NoOfDays-1 
    end 
    --declare @dateadded int = DATEDIFF(day,@BaseDate,@StartDate) 
    RETURN @StartDate 
END 

注:如果@StartDate落在週末,功能上面並沒有考慮改變@StartDate到下一個工作的日期。

+0

不錯的解決方案 - 可能不如其他一些選項那麼徹底,但是當精度不那麼重要時,較少的代碼行使其具有吸引力。 – cymorg