4

日付に特定の稼働日数を追加するSQLServer関数を作成する方法に関する投稿をいくつか見てきました。しかし、それらのどれも私が必要とする方法を正確に計算しません。現在コードで実行されている関数がありますが、ネイティブSQL Server関数に移動したいと思います(ストアドプロシージャとクエリで使用するため)。保管料の査定を開始する前に、お客様に貨物を収集するための5営業日を与えます。5営業日には、週末と休日は含まれません(休日の日付の表があります)。ここでの秘訣は、週末か休日かに関係なく、5営業日の直後に日付を取得する必要があるということです。そのため、最初の就業日ではなく、最後の就業日を返す関数が必要です。したがって、たとえば:

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営業日ですが、将来変更される可能性があるため、稼働日数をパラメータにする必要があります。また、この関数はかなり大きなデータセットで使用される可能性があるため、ループなしでこれを実行することをお勧めします。SQLServer2008を実行しています。

編集:これは、終了日を営業日にしたいので、「ループなしでSQLに今日までの営業日を追加する」の複製ではありません。終了日を最後の猶予日の直後の日付にします(つまり、月から金までの5営業日、次の月ではなく土の日付を返します)。

4

3 に答える 3

3
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日で十分です。必要に応じて増やしてください。

日付からより多くの営業日が必要な場合は、これを使用して1〜3年対応できます。

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
于 2012-10-12T15:45:38.223 に答える
2

以前にも同様の要件があったので、指定された日付に指定された日数(稼働日のみ)を追加した後に新しい日付を返す次の関数を共有すると、週末から土曜日を除外するオプションも提供されます。

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を次の稼働日に変更することを考慮しません。

このように宣言された休日のテーブルがある場合:

CREATE TABLE Holiday(ID INT IDENTIFY, HolidayDate Date, ...)

次に、関数は@StartDateを次のように次の稼働日に変更することを検討します

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) or
            exists (select 1 from Holiday where HolidayDate = convert(date, @StartDate))
        )
        begin
            set @StartDate = DateAdd(day,1,@StartDate)
        end
        set @NoOfDays = @NoOfDays-1
    end
    --declare @dateadded int = DATEDIFF(day,@BaseDate,@StartDate)
    RETURN @StartDate
END
于 2014-10-29T09:53:55.447 に答える
1

2つの日付の間のカウント就業日からのBogdanMaximとPeterMortensenへのすべてのクレジット。これは彼らの投稿です。関数に休日を追加しました(これは、日時フィールドが「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 --@EndDate 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)
*/
于 2013-11-22T16:13:17.117 に答える