例えば:
@dtBegin = '2012-06-29'
@input = 20
出力を。にしたい'2012-07-27'
。
私は自分のプロジェクトで同じ問題に取り組む必要がありました。Gordon Lionoffのソリューションは私を正しい軌道に乗せましたが、常に正しい結果をもたらすとは限りませんでした。週末から始まる日付も考慮に入れる必要があります。つまり、土曜日または日曜日に1営業日を追加すると、月曜日になります。これは、営業日の計算を処理するための最も一般的なアプローチです。
GordonLinoffの関数とPatrickMcDonaldの優れたC#に相当するものに基づいて独自のソリューションを作成しました
注:私のソリューションは、DATEFIRSTがデフォルト値の7に設定されている場合にのみ機能します。別のDATEFIRST値を使用する場合は、、、およびビットを変更する必要が1
あり7
ます(1,7,8,9,10)
。
私のソリューションは2つの機能で構成されています。エッジケースを処理する「外部」関数と実際の計算を実行する「内部」関数。どちらの関数もテーブル値関数であるため、実際には実装クエリに展開され、クエリオプティマイザを介して供給されます。
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
)
ご覧のとおり、「外部」関数は以下を処理します。
_
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
)
「内部」関数はGordonLinoffのソリューションに似ていますが、週末の境界を越える日付を考慮しますが、1週間の境界を越えることはありません。
最後に、関数をテストするためのテストスクリプトを作成しました。期待値は、Patrick McDonaldの優れたC#相当を使用して生成され、この人気のある計算機でこのデータをランダムに相互参照しました。
これは私が試したことです:
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
これは、カレンダー テーブルやユーザー定義関数に頼らずに実行できます。
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
)
これがきれいだと言っているのではありません。ただし、結合やループよりも算術の方が望ましい場合があります。