在SQL Server 2005中帮助缓慢的UDF

时间:2009-05-15 21:33:11

标签: sql recursion performance user-defined-functions

我有一个日期调用表[BadDates],它只有一列,其中每个记录都是要排除的日期。我有一个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。是否有其他方法可以更快的方法提供相同的功能。

非常感谢任何帮助!

5 个答案:

答案 0 :(得分:2)

我假设您要做的是计算在给定日期之后x个工作日的日期。什么日期是从今天起10个工作日。我还假设您的baddates表包含非工作日,例如周末和银行假期。

我在过去遇到过类似的要求,通常最后会有包含所有可能日期的天数表以及一个标志,指示某个特定日期是否为工作日。

然后我使用该表通过选择开始日期后x天的记录来计算从提供日期起x个工作日的日期。

这样的事情

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

其中day_state是
的值   D - 工作日
  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字段。

您应该在date和day_state上创建复合唯一索引。

答案 1 :(得分:2)

我没有对此进行测试,但理论上它应该可行。我把天数加起来。然后我检查该范围内是否有任何坏消息。如果有,我添加了坏天数,并检查我刚刚添加的范围内是否还有坏消息。重复,直到没有错误的日期。

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

答案 2 :(得分:0)

您可能希望在BadDates.StartDate上添加索引,但可能还有其他更好的解决方案。

答案 3 :(得分:0)

好的,为什么要计算何时可以使用EXISTS关键字?如果是因为你可以在Badates中有多个相同类型的日期,这似乎是错误的。 COUNT可能会查看整个表格来计算startdate的实例,只需要1即可排除。

您是否查看了查询计划以了解正在发生的事情?

答案 4 :(得分:0)

看起来您正在使用此UDF来计算两个日期之间的差异。如果我正确地解释了这个,那么我建议你使用内置的约会功能。