2つの日付間の就業日を数える


162

SQL Serverで2つの日付間の稼働日数を計算するにはどうすればよいですか?

月曜日から金曜日、T-SQLである必要があります。


5
就業日を定義できますか?月曜日から金曜日まで?主要な休日を除外しますか?どこの国?SQLで実行する必要がありますか?
Dave K

回答:


300

月曜日から金曜日までの平日は、次のように単一のSELECTでそれを行うことができます。

DECLARE @StartDate DATETIME
DECLARE @EndDate DATETIME
SET @StartDate = '2008/10/01'
SET @EndDate = '2008/10/31'


SELECT
   (DATEDIFF(dd, @StartDate, @EndDate) + 1)
  -(DATEDIFF(wk, @StartDate, @EndDate) * 2)
  -(CASE WHEN DATENAME(dw, @StartDate) = 'Sunday' THEN 1 ELSE 0 END)
  -(CASE WHEN DATENAME(dw, @EndDate) = 'Saturday' THEN 1 ELSE 0 END)

休日を含める場合は、少し工夫する必要があります...


3
このコードが常に機能するとは限らないことに気づきました。私はこれを試しました:SET @StartDate = '28 -mar-2011 'SET @EndDate = '29 -mar-2011'それは2日と数えた答え
ギリシャの治療

16
@greektreat正常に動作します。@StartDateと@EndDateの両方がカウントに含まれているだけです。月曜日から火曜日までを1日としてカウントする場合は、最初のDATEDIFFの後にある「+ 1」を削除します。次に、Fri-> Sat = 0、Fri-> Sun = 0、Fri-> Mon = 1も取得します。
Joe Daley、

6
@JoeDaleyのフォローアップとして。DATEDIFFの後に+ 1を削除して、開始日をカウントから除外する場合は、これのCASE部分も調整する必要があります。私はこれを使用してしまいました:+(CASE WHEN DATENAME(dw、@StartDate)= 'Saturday' THEN 1 ELSE 0 END)-(CASE WHEN DATENAME(dw、@EndDate)= 'Saturday' THEN 1 ELSE 0 END)
シーケンシア

7
datename関数はロケールに依存します。:より堅牢なだけでなく、より曖昧な解決策はで最後の2行を交換することである-(case datepart(dw, @StartDate)+@@datefirst when 8 then 1 else 0 end) -(case datepart(dw, @EndDate)+@@datefirst when 7 then 1 when 14 then 1 else 0 end)
トーベン・クライン

2
@Sequenziaのコメントを明確にするために、日曜日についてのケースステートメントを完全に削除し、唯一残します+(CASE WHEN DATENAME(dw, @StartDate) = 'Saturday' THEN 1 ELSE 0 END) - (CASE WHEN DATENAME(dw, @EndDate) = 'Saturday' THEN 1 ELSE 0 END)
Andy Raddatz

32

作業日数を計算しますが、このテーマについての良い記事を見つけることができますが、あなたが見ることができるとして、それはその高度ではありません。

--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)
        )
    END
GO

カスタムカレンダーを使用する必要がある場合は、いくつかのチェックといくつかのパラメーターを追加する必要があります。うまくいけば、それは良い出発点を提供するでしょう。


これがどのように機能するかを理解するためのリンクを含めてくれてありがとう。sqlservercentralへの書き込みは素晴らしかったです!
Chris Porter

20

Bogdan MaximとPeter Mortensenのすべての功績。これは彼らの投稿です、私は関数に休日を追加しました(これは、日時フィールド「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 [DB04\DB04].[Gateway].[dbo].[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)
*/

2
こんにちはDanB。バージョンtがテーブルtblHolidaysに土曜日と月曜日が含まれていないと想定していることをお知らせするために、時々発生します。とにかく、あなたのバージョンを共有してくれてありがとう。乾杯
Julio Nobre 2013年

3
フリオ-はい-私のバージョンでは、土曜日と日曜日(月曜日ではない)が週末であることを前提としているため、「営業日」ではありません。しかし、週末に作業している場合、私は毎日が「平日」であると思います。句の土曜日と日曜日の部分をコメントアウトして、すべての休日をtblHolidaysテーブルに追加できます。
Dan B

1
ダンに感謝します。これを関数に組み込んで、DateDimensionsテーブルにすべての日付、休日などが含まれるため、週末のチェックを追加しました。関数を使用して、次のように追加しました:IsWeekend = 0 where where [HolDate] between StartDate and EndDate)
AlsoKnownAsJazz

休日テーブルに週末の休日が含まれている場合、次のような基準を修正できますWHERE HolDate BETWEEN @StartDate AND @EndDate AND DATEPART(dw, HolDate) BETWEEN 2 AND 6。月曜日から金曜日までの休日のみをカウントします。
Andre

7

稼働日を計算する別のアプローチは、基本的に日付範囲を反復し、日が月曜日から金曜日の範囲内であることが判明するたびに1ずつ増分するWHILEループを使用することです。WHILEループを使用して稼働日を計算するための完全なスクリプトを以下に示します。

CREATE FUNCTION [dbo].[fn_GetTotalWorkingDaysUsingLoop]
(@DateFrom DATE,
@DateTo   DATE
)
RETURNS INT
AS
     BEGIN
         DECLARE @TotWorkingDays INT= 0;
         WHILE @DateFrom <= @DateTo
             BEGIN
                 IF DATENAME(WEEKDAY, @DateFrom) IN('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday')
                     BEGIN
                         SET @TotWorkingDays = @TotWorkingDays + 1;
                 END;
                 SET @DateFrom = DATEADD(DAY, 1, @DateFrom);
             END;
         RETURN @TotWorkingDays;
     END;
GO

WHILEループオプションはよりクリーンで、使用するコード行が少なくなりますが、特に日付範囲が数年にわたる場合、環境でパフォーマンスのボトルネックになる可能性があります。

この記事では、就業日と就業時間の計算方法に関する他の方法を参照できます。https//www.sqlshack.com/how-to-calculate-work-days-and-hours-in-sql-server/


6

を使用した関数として受け入れられた回答の私のバージョンなDATEPARTので、次の行で文字列比較を行う必要はありません

DATENAME(dw, @StartDate) = 'Sunday'

とにかく、ここに私のビジネスのdatediff関数があります

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

CREATE FUNCTION BDATEDIFF
(
    @startdate as DATETIME,
    @enddate as DATETIME
)
RETURNS INT
AS
BEGIN
    DECLARE @res int

SET @res = (DATEDIFF(dd, @startdate, @enddate) + 1)
    -(DATEDIFF(wk, @startdate, @enddate) * 2)
    -(CASE WHEN DATEPART(dw, @startdate) = 1 THEN 1 ELSE 0 END)
    -(CASE WHEN DATEPART(dw, @enddate) = 7 THEN 1 ELSE 0 END)

    RETURN @res
END
GO

5
 DECLARE @TotalDays INT,@WorkDays INT
 DECLARE @ReducedDayswithEndDate INT
 DECLARE @WeekPart INT
 DECLARE @DatePart INT

 SET @TotalDays= DATEDIFF(day, @StartDate, @EndDate) +1
 SELECT @ReducedDayswithEndDate = CASE DATENAME(weekday, @EndDate)
  WHEN 'Saturday' THEN 1
  WHEN 'Sunday' THEN 2
  ELSE 0 END 
 SET @TotalDays=@TotalDays-@ReducedDayswithEndDate
 SET @WeekPart=@TotalDays/7;
 SET @DatePart=@TotalDays%7;
 SET @WorkDays=(@WeekPart*5)+@DatePart

 RETURN @WorkDays

コード、XML、またはデータサンプルを投稿する場合は、テキストエディターでそれらの行強調表示し、エディターのツールバーの[コードサンプル]ボタン({})をクリックして、フォーマットと構文を強調表示してください。
marc_s

素晴らしい、周辺機能やこれを使用したデータベースの更新は必要ありません。ありがとう。ソルティアが大好きです:-)
ブライアンスコット

スーパーソリューション。次のように2つのテーブル列の日付間の平日(MF)を計算するためにwebiユニバースで使用する変数の式を下塗りしました...(((((DATEDIFF(day、table.col1、table.col2)+1)- ((CASE DATENAME(weekday、table.col2)WHEN 'Saturday' THEN 1 WHEN 'Sunday' THEN 2 ELSE 0 END)))/ 7)* 5)+(((DATEDIFF(day、table.col1、table.col2 )+1)-((CASE DATENAME(weekday、table.col2)WHEN 'Saturday' THEN 1 WHEN 'Sunday' THEN 2 ELSE 0 END)))%7)
ヒラリー

5

(私はコメント権限が恥ずかしい数点です)

CMSのエレガントなソリューションで+1日を忘れる場合は、開始日と終了日が同じ週末の場合、否定的な回答が返されることに注意してください。つまり、2008/10/26から2008/10/26は-1を返します。

私のかなり単純化した解決策:

select @Result = (..CMS's answer..)
if  (@Result < 0)
        select @Result = 0
    RETURN @Result

..また、終了日より後の開始日を含むすべての誤った投稿をゼロに設定します。あなたが探しているかもしれないし、探していないかもしれないもの。


5

休日を含む日付の違いについては、私はこのように行きました:

1)休日のあるテーブル:

    CREATE TABLE [dbo].[Holiday](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](50) NULL,
[Date] [datetime] NOT NULL)

2)私の計画テーブルは次のようになっていて、空のWork_Days列に入力したい:

    CREATE TABLE [dbo].[Plan_Phase](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Id_Plan] [int] NOT NULL,
[Id_Phase] [int] NOT NULL,
[Start_Date] [datetime] NULL,
[End_Date] [datetime] NULL,
[Work_Days] [int] NULL)

3) "Work_Days"を後で取得できるようにするには、次のようにする必要があります。

SELECT Start_Date, End_Date,
 (DATEDIFF(dd, Start_Date, End_Date) + 1)
-(DATEDIFF(wk, Start_Date, End_Date) * 2)
-(SELECT COUNT(*) From Holiday Where Date  >= Start_Date AND Date <= End_Date)
-(CASE WHEN DATENAME(dw, Start_Date) = 'Sunday' THEN 1 ELSE 0 END)
-(CASE WHEN DATENAME(dw, End_Date) = 'Saturday' THEN 1 ELSE 0 END)
-(CASE WHEN (SELECT COUNT(*) From Holiday Where Start_Date  = Date) > 0 THEN 1 ELSE 0 END)
-(CASE WHEN (SELECT COUNT(*) From Holiday Where End_Date  = Date) > 0 THEN 1 ELSE 0 END) AS Work_Days
from Plan_Phase

お役に立てれば幸いです。

乾杯


1
あなたの休日の減算について。開始日が1月1日で終了日が12月31日の場合はどうなりますか?2のみを減算しますが、これは誤りです。「SELECT COUNT(*)FROM Holiday ...」ではなく、DATEDIFF(day、Start_Date、Date)をEnd_Dateに使用することをお勧めします。
Illia Ratkevych 2013年

4

これはうまく機能するバージョンです(私はそう思います)。休日テーブルには、会社が観察する休日を含むHoliday_date列が含まれています。

DECLARE @RAWDAYS INT

   SELECT @RAWDAYS =  DATEDIFF(day, @StartDate, @EndDate )--+1
                    -( 2 * DATEDIFF( week, @StartDate, @EndDate ) )
                    + CASE WHEN DATENAME(dw, @StartDate) = 'Saturday' THEN 1 ELSE 0 END
                    - CASE WHEN DATENAME(dw, @EndDate) = 'Saturday' THEN 1 ELSE 0 END 

   SELECT  @RAWDAYS - COUNT(*) 
     FROM HOLIDAY NumberOfBusinessDays
    WHERE [Holiday_Date] BETWEEN @StartDate+1 AND @EndDate 

これらの休日の日付は週末にも当てはまる場合があります。また、日曜日の休日が次の月曜日に変わる人もいます。
イラワンSoetomo 2016年

3

これは古い質問ですが、いくつかのアイテムがあり、正しく蓄積するために日数が必要なため、開始日を除く就業日の式が必要でした。

非反復的な答えはどれもうまくいきませんでした。

私は次のような定義を使用しました

真夜中から月曜日、火曜日、水曜日、木曜日、金曜日までの回数

(他の人は月曜日の代わりに真夜中から土曜日までを数えるかもしれません)

私はこの式で終わった

SELECT DATEDIFF(day, @StartDate, @EndDate) /* all midnights passed */
     - DATEDIFF(week, @StartDate, @EndDate) /* remove sunday midnights */
     - DATEDIFF(week, DATEADD(day, 1, @StartDate), DATEADD(day, 1, @EndDate)) /* remove saturday midnights */

1
それは私のためにそれをしましたが、私は小さな変更をしなければなりませんでした。@StartDate土曜日または金曜日がいつであるかは考慮されていませんでした。これが私のバージョンですDATEDIFF(day, @StartDate, @EndDate) - DATEDIFF(week, @StartDate, @EndDate) - DATEDIFF(week, DATEADD(day, 1, @StartDate), DATEADD(day, 1, @EndDate)) - (CASE WHEN DATEPART(WEEKDAY, @StartDate) IN (1, 7) THEN 1 ELSE 0 END) + 1
。– caiosm1005

@ caiosm1005、土曜日から日曜日は0を返し、土曜日から月曜日は1を返します。金曜日から土曜日は0を返します。すべては私の定義と一致しています。コードは正しく蓄積されません(たとえば、金曜日から金曜日は6を返しますが、月曜日から月曜日は5を
返します

3

これは基本的に、特定の言語設定に依存しないCMSの答えです。そして、ジェネリックを対象としているため、すべての@@datefirst設定で機能するはずです。

datediff(day, <start>, <end>) + 1 - datediff(week, <start>, <end>) * 2
    /* if start is a Sunday, adjust by -1 */
  + case when datepart(weekday, <start>) = 8 - @@datefirst then -1 else 0 end
    /* if end is a Saturday, adjust by -1 */
  + case when datepart(weekday, <end>) = (13 - @@datefirst) % 7 + 1 then -1 else 0 end

datediff(week, ...)は常に週の土曜日から日曜日の境界を使用するため、式は確定的であり、変更する必要はありません(平日の定義が一貫して月曜日から金曜日までである限り)。日の番号付けは、@@datefirst設定と修正された計算は、いくつかのモジュラー演算の小さな複雑さでこの修正を処理します。

土曜日/日曜日の問題に対処するためのより明確な方法は、曜日の値を抽出する前に日付を変換することです。シフト後、値は、日曜日の1から始まり、土曜日の7で終わる固定された(そしておそらくもっとよく知られている)番号付けに戻ります。

datediff(day, <start>, <end>) + 1 - datediff(week, <start>, <end>) * 2
  + case when datepart(weekday, dateadd(day, @@datefirst, <start>)) = 1 then -1 else 0 end
  + case when datepart(weekday, dateadd(day, @@datefirst, <end>))   = 7 then -1 else 0 end

私は、この形式のソリューションを少なくとも2002年まで追跡し、Itzik Ben-Ganの記事を追跡しました。(https://technet.microsoft.com/en-us/library/aa175781(v=sql.80).aspx)新しいdate型は日付の計算を許可しないため、小さな調整が必要ですが、それ以外は同じです。

編集:+1どういうわけか中断されていたものを追加し直しました。また、このメソッドは常に開始日と終了日をカウントすることにも注意してください。また、終了日が開始日以降であると想定しています。


これは週末の多くの日付で誤った結果を返すため、それらが加算されないことに注意してください(金->月は金->土+土->日+日->月と同じである必要があります)。Fri-> Satは0(正しい)、Sat-> Sunは0(間違った-1)、Sun-> Monは1(間違った0)でなければなりません。これに続くその他のエラーは、土曜日->土曜日= -1、日曜日->日曜日= -1、
日曜日

@adrianm私は問題を修正したと思います。実際問題は、なんとかその部品を誤って落としたため、常に1つずれるということでした。
shawnt00

更新していただきありがとうございます。私はあなたの式が私が必要とするものである開始日を除外していると思いました。自分で解決して別の回答として追加しました。
adrianm 2018

2

日付テーブルを使用する:

    DECLARE 
        @StartDate date = '2014-01-01',
        @EndDate date = '2014-01-31'; 
    SELECT 
        COUNT(*) As NumberOfWeekDays
    FROM dbo.Calendar
    WHERE CalendarDate BETWEEN @StartDate AND @EndDate
      AND IsWorkDay = 1;

それがない場合は、数値表を使用できます。

    DECLARE 
    @StartDate datetime = '2014-01-01',
    @EndDate datetime = '2014-01-31'; 
    SELECT 
    SUM(CASE WHEN DATEPART(dw, DATEADD(dd, Number-1, @StartDate)) BETWEEN 2 AND 6 THEN 1 ELSE 0 END) As NumberOfWeekDays
    FROM dbo.Numbers
    WHERE Number <= DATEDIFF(dd, @StartDate, @EndDate) + 1 -- Number table starts at 1, we want a 0 base

どちらも高速である必要があり、あいまいさ/複雑さを排除します。最初のオプションが最適ですが、カレンダーテーブルがない場合は、常にCTEを使用して数値テーブルを作成できます。


1
DECLARE @StartDate datetime,@EndDate datetime

select @StartDate='3/2/2010', @EndDate='3/7/2010'

DECLARE @TotalDays INT,@WorkDays INT

DECLARE @ReducedDayswithEndDate INT

DECLARE @WeekPart INT

DECLARE @DatePart INT

SET @TotalDays= DATEDIFF(day, @StartDate, @EndDate) +1

SELECT @ReducedDayswithEndDate = CASE DATENAME(weekday, @EndDate)
    WHEN 'Saturday' THEN 1
    WHEN 'Sunday' THEN 2
    ELSE 0 END

SET @TotalDays=@TotalDays-@ReducedDayswithEndDate

SET @WeekPart=@TotalDays/7;

SET @DatePart=@TotalDays%7;

SET @WorkDays=(@WeekPart*5)+@DatePart

SELECT @WorkDays

あなたが機能を使用しようとしている場合、それはのように、テーブルベースの機能と一緒に行く方が良いかもしれませんマリオMeyrellesによって答え
ジェームズ・ジェンキンス

1
CREATE FUNCTION x
(
    @StartDate DATETIME,
    @EndDate DATETIME
)
RETURNS INT
AS
BEGIN
    DECLARE @Teller INT

    SET @StartDate = DATEADD(dd,1,@StartDate)

    SET @Teller = 0
    IF DATEDIFF(dd,@StartDate,@EndDate) <= 0
    BEGIN
        SET @Teller = 0 
    END
    ELSE
    BEGIN
        WHILE
            DATEDIFF(dd,@StartDate,@EndDate) >= 0
        BEGIN
            IF DATEPART(dw,@StartDate) < 6
            BEGIN
                SET @Teller = @Teller + 1
            END
            SET @StartDate = DATEADD(dd,1,@StartDate)
        END
    END
    RETURN @Teller
END

1

ここではさまざまな例を取り上げましたが、私の特定の状況では、配達用に@PromisedDateがあり、アイテムの実際の受け取り用に@ReceivedDateがあります。「PromisedDate」より前にアイテムを受け取った場合、関数に渡された日付をカレンダー順に並べ替えないと、計算が正しく合計されませんでした。毎回日付をチェックしたくないので、これを処理するように関数を変更しました。

Create FUNCTION [dbo].[fnGetBusinessDays]
(
 @PromiseDate date,
 @ReceivedDate date
)
RETURNS integer
AS
BEGIN
 DECLARE @days integer

 SELECT @days = 
    Case when @PromiseDate > @ReceivedDate Then
        DATEDIFF(d,@PromiseDate,@ReceivedDate) + 
        ABS(DATEDIFF(wk,@PromiseDate,@ReceivedDate)) * 2 +
        CASE 
            WHEN DATENAME(dw, @PromiseDate) <> 'Saturday' AND DATENAME(dw, @ReceivedDate) = 'Saturday' THEN 1 
            WHEN DATENAME(dw, @PromiseDate) = 'Saturday' AND DATENAME(dw, @ReceivedDate) <> 'Saturday' THEN -1 
            ELSE 0
        END +
        (Select COUNT(*) FROM CompanyHolidays 
            WHERE HolidayDate BETWEEN @ReceivedDate AND @PromiseDate 
            AND DATENAME(dw, HolidayDate) <> 'Saturday' AND DATENAME(dw, HolidayDate) <> 'Sunday')
    Else
        DATEDIFF(d,@PromiseDate,@ReceivedDate)  -
        ABS(DATEDIFF(wk,@PromiseDate,@ReceivedDate)) * 2  -
            CASE 
                WHEN DATENAME(dw, @PromiseDate) <> 'Saturday' AND DATENAME(dw, @ReceivedDate) = 'Saturday' THEN 1 
                WHEN DATENAME(dw, @PromiseDate) = 'Saturday' AND DATENAME(dw, @ReceivedDate) <> 'Saturday' THEN -1 
                ELSE 0
            END -
        (Select COUNT(*) FROM CompanyHolidays 
            WHERE HolidayDate BETWEEN @PromiseDate and @ReceivedDate 
            AND DATENAME(dw, HolidayDate) <> 'Saturday' AND DATENAME(dw, HolidayDate) <> 'Sunday')
    End


 RETURN (@days)

END

1

特定の日付に稼働日を追加する必要がある場合は、以下で説明するカレンダーテーブルに依存する関数を作成できます。

CREATE TABLE Calendar
(
  dt SMALLDATETIME PRIMARY KEY, 
  IsWorkDay BIT
);

--fill the rows with normal days, weekends and holidays.


create function AddWorkingDays (@initialDate smalldatetime, @numberOfDays int)
    returns smalldatetime as 

    begin
        declare @result smalldatetime
        set @result = 
        (
            select t.dt from
            (
                select dt, ROW_NUMBER() over (order by dt) as daysAhead from calendar 
                where dt > @initialDate
                and IsWorkDay = 1
                ) t
            where t.daysAhead = @numberOfDays
        )

        return @result
    end


1

DATEDIFFと同様に、終了日は間隔の一部とは見なしません。(たとえば)@StartDateと@EndDateの間の日曜日の数は、「最初の」月曜日と@EndDateの間の日曜日の数から、この「最初の」月曜日と@StartDateの間の日曜日の数を引いた数です。これを知っていれば、次のように就業日数を計算できます。

DECLARE @StartDate DATETIME
DECLARE @EndDate DATETIME
SET @StartDate = '2018/01/01'
SET @EndDate = '2019/01/01'

SELECT DATEDIFF(Day, @StartDate, @EndDate) -- Total Days
  - (DATEDIFF(Day, 0, @EndDate)/7 - DATEDIFF(Day, 0, @StartDate)/7) -- Sundays
  - (DATEDIFF(Day, -1, @EndDate)/7 - DATEDIFF(Day, -1, @StartDate)/7) -- Saturdays

宜しくお願いします!


パーフェクト!これは私が探していたものです。特別な感謝!
ファントム

0

それは私のために働いています、私の国の土曜日と日曜日は非稼働日です。

@StartDateと@EndDateの時間は私にとって重要です。

CREATE FUNCTION [dbo].[fnGetCountWorkingBusinessDays]
(
    @StartDate as DATETIME,
    @EndDate as DATETIME
)
RETURNS INT
AS
BEGIN
    DECLARE @res int

SET @StartDate = CASE 
    WHEN DATENAME(dw, @StartDate) = 'Saturday' THEN DATEADD(dd, 2, DATEDIFF(dd, 0, @StartDate))
    WHEN DATENAME(dw, @StartDate) = 'Sunday' THEN DATEADD(dd, 1, DATEDIFF(dd, 0, @StartDate))
    ELSE @StartDate END

SET @EndDate = CASE 
    WHEN DATENAME(dw, @EndDate) = 'Saturday' THEN DATEADD(dd, 0, DATEDIFF(dd, 0, @EndDate))
    WHEN DATENAME(dw, @EndDate) = 'Sunday' THEN DATEADD(dd, -1, DATEDIFF(dd, 0, @EndDate))
    ELSE @EndDate END


SET @res =
    (DATEDIFF(hour, @StartDate, @EndDate) / 24)
  - (DATEDIFF(wk, @StartDate, @EndDate) * 2)

SET @res = CASE WHEN @res < 0 THEN 0 ELSE @res END

    RETURN @res
END

GO

0

次のような関数を作成します。

CREATE FUNCTION dbo.fn_WorkDays(@StartDate DATETIME, @EndDate DATETIME= NULL )
RETURNS INT 
AS
BEGIN
       DECLARE @Days int
       SET @Days = 0

       IF @EndDate = NULL
              SET @EndDate = EOMONTH(@StartDate) --last date of the month

       WHILE DATEDIFF(dd,@StartDate,@EndDate) >= 0
       BEGIN
              IF DATENAME(dw, @StartDate) <> 'Saturday' 
                     and DATENAME(dw, @StartDate) <> 'Sunday' 
                     and Not ((Day(@StartDate) = 1 And Month(@StartDate) = 1)) --New Year's Day.
                     and Not ((Day(@StartDate) = 4 And Month(@StartDate) = 7)) --Independence Day.
              BEGIN
                     SET @Days = @Days + 1
              END

              SET @StartDate = DATEADD(dd,1,@StartDate)
       END

       RETURN  @Days
END

次のような関数を呼び出すことができます。

select dbo.fn_WorkDays('1/1/2016', '9/25/2016')

または次のように:

select dbo.fn_WorkDays(StartDate, EndDate) 
from table1

0
Create Function dbo.DateDiff_WeekDays 
(
@StartDate  DateTime,
@EndDate    DateTime
)
Returns Int
As

Begin   

Declare @Result Int = 0

While   @StartDate <= @EndDate
Begin 
    If DateName(DW, @StartDate) not in ('Saturday','Sunday')
        Begin
            Set @Result = @Result +1
        End
        Set @StartDate = DateAdd(Day, +1, @StartDate)
End

Return @Result

終わり


0

以下のTSQLはかなり洗練されたソリューションであることがわかりました(関数を実行する権限がありません)。私が見つけたDATEDIFF無視DATEFIRST週の最初の日を月曜日にしたいと。また、最初の稼働日をゼロに設定したかったのですが、週末になると月曜日がゼロになります。これは、少し異なる要件を持つ人を助けるかもしれません:)

祝日は取り扱いません

SET DATEFIRST 1
SELECT
,(DATEDIFF(DD,  [StartDate], [EndDate]))        
-(DATEDIFF(wk,  [StartDate], [EndDate]))        
-(DATEDIFF(wk, DATEADD(dd,-@@DATEFIRST,[StartDate]), DATEADD(dd,-@@DATEFIRST,[EndDate]))) AS [WorkingDays] 
FROM /*Your Table*/ 

0

1つのアプローチは、日が土曜日または日曜日でないかどうかをチェックし、フラグを立てるケース式(最初は平日は1、週末は0)と組み合わせて、開始から終了まで「日付を歩く」ことです。最後に、フラグを合計して(他のフラグは0なので、1フラグの数と同じになります)、平日の数を取得します。

GetNums(startNumber、endNumber)タイプのユーティリティ関数を使用して、開始日から終了日まで「ループ」する一連の数値を生成できます。実装については、http://tsql.solidq.com/SourceCodes/GetNums.txtを参照してください。ロジックを拡張して休日に対応することもできます(たとえば、休日テーブルがある場合)。

declare @date1 as datetime = '19900101'
declare @date2 as datetime = '19900120'

select  sum(case when DATENAME(DW,currentDate) not in ('Saturday', 'Sunday') then 1 else 0 end) as noOfWorkDays
from dbo.GetNums(0,DATEDIFF(day,@date1, @date2)-1) as Num
cross apply (select DATEADD(day,n,@date1)) as Dates(currentDate)

0

私は他の人からいくつかのアイデアを借りて私のソリューションを作成しました。インラインコードを使用して、週末と米国連邦の祝日を無視しています。私の環境では、EndDateはnullの可能性がありますが、StartDateより前になることはありません。

CREATE FUNCTION dbo.ufn_CalculateBusinessDays(
@StartDate DATE,
@EndDate DATE = NULL)

RETURNS INT
AS

BEGIN
DECLARE @TotalBusinessDays INT = 0;
DECLARE @TestDate DATE = @StartDate;


IF @EndDate IS NULL
    RETURN NULL;

WHILE @TestDate < @EndDate
BEGIN
    DECLARE @Month INT = DATEPART(MM, @TestDate);
    DECLARE @Day INT = DATEPART(DD, @TestDate);
    DECLARE @DayOfWeek INT = DATEPART(WEEKDAY, @TestDate) - 1; --Monday = 1, Tuesday = 2, etc.
    DECLARE @DayOccurrence INT = (@Day - 1) / 7 + 1; --Nth day of month (3rd Monday, for example)

    --Increment business day counter if not a weekend or holiday
    SELECT @TotalBusinessDays += (
        SELECT CASE
            --Saturday OR Sunday
            WHEN @DayOfWeek IN (6,7) THEN 0
            --New Year's Day
            WHEN @Month = 1 AND @Day = 1 THEN 0
            --MLK Jr. Day
            WHEN @Month = 1 AND @DayOfWeek = 1 AND @DayOccurrence = 3 THEN 0
            --G. Washington's Birthday
            WHEN @Month = 2 AND @DayOfWeek = 1 AND @DayOccurrence = 3 THEN 0
            --Memorial Day
            WHEN @Month = 5 AND @DayOfWeek = 1 AND @Day BETWEEN 25 AND 31 THEN 0
            --Independence Day
            WHEN @Month = 7 AND @Day = 4 THEN 0
            --Labor Day
            WHEN @Month = 9 AND @DayOfWeek = 1 AND @DayOccurrence = 1 THEN 0
            --Columbus Day
            WHEN @Month = 10 AND @DayOfWeek = 1 AND @DayOccurrence = 2 THEN 0
            --Veterans Day
            WHEN @Month = 11 AND @Day = 11 THEN 0
            --Thanksgiving
            WHEN @Month = 11 AND @DayOfWeek = 4 AND @DayOccurrence = 4 THEN 0
            --Christmas
            WHEN @Month = 12 AND @Day = 25 THEN 0
            ELSE 1
            END AS Result);

    SET @TestDate = DATEADD(dd, 1, @TestDate);
END

RETURN @TotalBusinessDays;
END
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.