2009-05-15 15 views
1

私は日付のテーブルを[BadDates]と呼んでいます。すべてのレコードが除外する日付の列が1つだけあります。SQL Server 2005の低速UDFのヘルプ

CREATE FUNCTION [dbo].[udf_GetDateInBusinessDays] 
(
    @StartDate datetime, --Start Date 
    @NumberDays int   --Good days ahead 
) 
RETURNS datetime 
AS 
BEGIN 
-- Declare the return variable here 
DECLARE @ReturnDate datetime 
SET @ReturnDate = @StartDate 
DECLARE @Counter int 
SET @Counter = 0 
WHILE @Counter < @NumberDays 
BEGIN 
    SET @ReturnDate = DateAdd(d,1,@ReturnDate) 
    IF ((SELECT COUNT(ID) 
     FROM dbo.[BadDates] 
     WHERE StartDate = @ReturnDate) = 0) 
    BEGIN 
     SET @Counter = @Counter + 1 
    END 
END 
RETURN @ReturnDate 
END 

このUDFは素晴らしい作品が、それは、処理が遅いですが、次のように私は、UDFを持っています。これを使用するストアドプロシージャは、すべてのレコードでUDFを実行します。この同じ機能をより高速な方法で提供する他の方法はありますか?

ご協力いただきありがとうございます。

+3

あなたは達成しようとしていることを高レベルの英語で記述できますか? –

答えて

2

これはテストしていませんが、理論的にはうまくいくはずです。私は日数を加えます。次に、その範囲にbaddatesがあるかどうかを確認します。もし私が悪い日の数を加えて、私が今追加した範囲にもうbaddatesがあるかどうかをチェックしてください。悪い日まで繰り返す。

CREATE FUNCTION [dbo].[udf_GetDateInBusinessDays] 
(
    @StartDate datetime, --Start Date 
    @NumberDays int   --Good days ahead 
) 
RETURNS datetime 
AS 
BEGIN 
-- Declare the return variable here 
DECLARE @ReturnDate datetime 
SET @ReturnDate = dateadd(d, @NumberDays, @StartDate); 


DECLARE @d int; 
SET @d = (select count(1) from baddates where startdate >= @StartDate and startdate <= @ReturnDate); 

declare @t datetime; 

WHILE @d > 0 
BEGIN 
    set @t = @ReturnDate; 
    set @ReturnDate = dateadd(d, @d, @ReturnDate); 
    SET @d = (select count(1) from baddates where startdate > @t and startdate <= @ReturnDate); 
END 

RETURN @ReturnDate 
END 
+0

+1これは私の次のアプローチになるだろう –

+0

これは私のために働いていましたが、まだ超高速ではありませんでしたが、私の質問は6分以上から1/2分に至りました。 ありがとうございます!!!!! –

+0

あなたの歓迎、私は少しきれいにするつもりです。私は、集計関数は常に行を返すことを常に忘れています。だからisnullは本当に必要ではありません。 – dotjoe

0

BadDates.StartDateにインデックスを付けることができますが、他にも優れた解決策があります。

0

OkなぜEXISTSキーワードを使用することができますか?あなたがBadatesで同じタイプの複数の日付を持つことができるので、これは間違っているようです。 COUNTは、必要なものすべてを除外するために必要なときに、startdateのインスタンスを数えるためにテーブル全体を調べます。

何が起こっているかを確認するためにクエリプランを見ましたか?

+0

Iは を交換しようとした場合(DBO FROM(SELECT COUNT(ID) 。[BadDates] 開始日= @ReturnDate)= 0) と IF(NOT EXISTS(DBOからID を選択します。[BadDates] 開始日= @返された日付)) しかしそれはずっと助けにはならなかった、2秒の改良。 –

0

このUDFを使用して2つの日付の差を計算しているようです。私はこれを正しく解釈しているなら、あなたは組み込み関数を使うことをお勧めします。

+0

いいえ、違いはありません。基本的に@StartDateが2009年1月1日と@NumberDays = 3に等しい場合、それは1/2/2009がBadDatesにあるかどうかを調べ、そうでなければカウンタをインクリメントします。そうでなければ、1/3/2009に移動します。これは3つの良い日付が増分されるまで起こります。 2009年1月3日がbaddatesの唯一の日付だった場合、出力は1/5/2009になります。 –

+0

それで、悪い日付の特定の日付範囲のレコード数を照会し、必要に応じてdateaddを使用して日付をインクリメントするのはなぜですか? –

2

私はあなたがしようとしていることは、指定された日付を過ぎてx営業日である日付を計算すると仮定しています。今日から10営業日かかりますか?私はあなたのbaddatesテーブルには休業日が含まれていると仮定しています。土日祝日

私は過去に同様の要件に遭遇しました。通常、特定の日付が稼働日であるかどうかを示すフラグと共に、すべての可能な日付を含むdaysテーブルで終了しました。

次に、そのテーブルを使用して、開始日からx日後のレコードを選択して、提供された日付からx就業日を計算します。

のでday_stateが
Dの値であり、この

CREATE TABLE all_days ( 
    dated DATETIME, 
    day_state CHAR(1) 
) 

のようなもの - 営業日
W - 週末
B - バンクホリデー

のx加工後の日付を見つけるためのSQL日数は

SELECT MAX(dated) 
FROM (
    SELECT TOP(@number_days) dated 
    FROM all_days 
    WHERE day_state = 'D' 
    AND dated >= @start_date 
    ORDER by dated ASC 
) 

このコードはテストされていませんが、一般的な考え方を与える必要があります。週末と祝日を区別したくない場合は、day_stateの名前をworking_dayに変更してBITフィールドにすることができます。

datedおよびday_stateに複合一意索引を作成する必要があります。

+0

これはそれです、そして古いバージョンは言及されたのと同じように動作します。残念ながら、新しい要件は、良いものではなく悪い日付だけを格納することです。 –

+0

多分誰でも設計仕様書にSOをチェックしなければならないかもしれません - 20個のWTFポイントを授与します。 – dkretz

+0

私は要件がいつもと同じだと思いますが、誰かが別の実装を見たいと思うのは、おそらく、フル・デイズ・テーブルを維持するという痛みのためです。 –