2009-05-15 2 views
1

나는 [BadDates]라는 날짜 테이블을 가지고 있는데, 여기에는 모든 레코드가 제외 할 날짜 인 하나의 열만 있습니다. 다음과 같은 UDF가 있습니다.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를 실행합니다. 더 빠른 방법으로 동일한 기능을 제공하는 다른 방법이 있습니까?

도움을 주시면 대단히 감사하겠습니다.

+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 분 30 분으로 걸렸습니다. 감사합니다 !!!!! –

+0

안녕하세요. 조금만 청소하겠습니다. 나는 항상 집계 함수가 항상 행을 반환한다는 것을 잊는다. 따라서 isnull은 실제로 필요하지 않습니다. – dotjoe

0

BadDates.StartDate에 인덱스를 넣을 수 있지만 다른 더 나은 솔루션이있을 수 있습니다.

0

좋아요. EXISTS 키워드를 사용할 수있는 이유는 무엇입니까? 왜냐하면 Badates에서 같은 유형의 여러 날짜를 가질 수 있기 때문에 이것이 틀린 것 같습니다. COUNT는 시작일의 인스턴스를 계산하기 위해 전체 테이블을 조사 할 것입니다. 필요한 경우 모두 1을 제외하십시오.

무슨 일이 일어나고 있는지 쿼리 계획을 살펴 보셨습니까?

+0

I는 교체하려고 IF (DBO FROM (SELECT COUNT (ID) . BadDates] STARTDATE = @ReturnDate) = 0) 와 IF (NOT EXISTS (DBO로부터 ID 를 선택. BadDates] STARTDATE = @ReturnDate)) 하지만 그다지 도움이되지 못했습니다. 2 초의 improvment. –

0

이 UDF를 사용하여 두 날짜의 차이를 계산하는 것처럼 보입니다. 이 올바르게 해석하는 경우 내장 된 datediff 함수를 사용하는 것이 좋습니다.

+0

아니요, 아니요. 기본적으로 @StartDate가 1/1/2009와 같고 @NumberDays = 3 인 경우 1/2/2009가 BadDates에 있는지, 그렇지 않으면 카운터가 증가할지, 그렇지 않으면 1/3/2009로 이동합니다. . 이것은 3 개의 좋은 날짜가 증가 될 때까지 일어날 것입니다. 2009 년 1 월 3 일이 baddates의 유일한 날짜 인 경우 출력은 1/5/2009입니다. –

+0

잘못된 날짜의 주어진 날짜 범위에있는 레코드 수를 쿼리 한 다음 필요하면 dateadd를 사용하여 날짜를 늘리는 것이 어떻습니까? –

2

나는 당신이하려고하는 것이 주어진 날짜보다 x 근무일 인 날짜를 계산한다고 가정하고 있습니다. 어떤 날이 오늘부터 10 근무일입니까? 나는 또한 귀하의 baddates 테이블에 비 근무 일이 포함되어 있다고 가정합니다. 주말 및 공휴일.

이전에는 비슷한 요구 사항이 발생했으며 일반적으로 특정 날짜가 근무일인지 아닌지 여부를 나타내는 플래그와 함께 모든 가능한 날짜가 포함 된 일 테이블이 종료되었습니다.

그런 다음 해당 테이블을 사용하여 시작일로부터 x 일 후에 레코드를 선택하여 제공된 날짜의 x 근무일을 계산합니다.

그래서 day_state이
D의 값이

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

같은 - 워킹 데이
W - 주말
B - 은행 휴무일

는 SQL은 X의 작업 후 날짜를 찾을 수 일이되면

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

필자는 요구 사항이 항상 같다고 생각하지만 누군가는 다른 구현을보고 싶어합니다. 아마도 전체 일 테이블을 유지하는 데 따른 고통 때문에. –