递归CTE的功能似乎很慢

时间:2011-02-24 17:48:21

标签: sql sql-server-2005 tsql common-table-expression

我目前正在开发一个使用递归CTE的函数,但似乎性能很差。我需要它在函数中(所以没有临时表)所以我可以在存储过程中轻松使用它。

以下是代码:

CREATE FUNCTION [dbo].[Web_GetDailyLoadListUDF]
(
    @CustomerID INT
    , @StartDate DATETIME
    , @Days INT
    , @IncludeChildren BIT
)
RETURNS @TableOfValues TABLE 
(  
    RowID SMALLINT IDENTITY(1,1)
    , DailyLoadCount INT
    , DailyLoadDate VARCHAR(6)
    , FullDate DATETIME
) 
AS
BEGIN
    DECLARE @MaxDate DATETIME;
    SET @MaxDate = DATEADD(dd, @Days * -1.7, DATEDIFF(dd, 0, @StartDate));

    WITH DateCTE AS
    (
        SELECT DATEADD(dd, 0, DATEDIFF(dd, 0, @StartDate)) AS DateValue
        UNION ALL
        SELECT DATEADD(DAY, -1, DateValue)
        FROM DateCTE
        WHERE DATEADD(DAY, -1, DateValue) > @MaxDate
    )
    INSERT INTO @TableOfValues
    SELECT * FROM
    (
        SELECT TOP (@Days)
            (
                SELECT COUNT(*)
                FROM dbo.[Load] l WITH (NOLOCK)
                JOIN dbo.LoadCustomer lc WITH (NOLOCK)
                    ON lc.LoadID = l.ID
                JOIN dbo.Customer c WITH (NOLOCK)
                    ON c.ID = lc.CustomerID
                WHERE DATEADD(dd, 0, DATEDIFF(dd, 0, l.LoadDate)) = dct.DateValue
                    AND l.StateType = 1
                    AND lc.Main = 1
                    AND (c.ID = @CustomerID OR (@IncludeChildren = 1 AND c.ParentCustomerID = @CustomerID))
            ) AS DailyLoadCount
            , CONVERT(VARCHAR(6), dct.DateValue, 107) AS DailyLoadDate
            , dct.DateValue
        FROM DateCTE dct
        WHERE
            DATEPART(DW, dct.DateValue) NOT IN (1, 7)
            AND dct.DateValue NOT IN
            (
                SELECT HolidayDate FROM Holiday
            )
        ORDER BY dct.DateValue DESC
    ) AS S
    ORDER BY s.DateValue ASC

    RETURN
END

这个SQL应该检索的是过去工作日的@Days(没有周末/假日)每天的加载次数。

我基本上只需要一些帮助来优化它,这样它就不会运行得那么慢。 (每个客户最多需要20秒,这将被称为数千人)。

3 个答案:

答案 0 :(得分:3)

你的主要问题就在这里

WHERE DATEADD(dd, 0, DATEDIFF(dd, 0, l.LoadDate)) = dct.DateValue

应该是

WHERE l.LoadDate >= dct.DateValue
  AND l.LoadDate <  dct.DateValue +1

在Load(LoadDate,ID)和Load(ID,LoadDate)上创建复合索引,并删除未在查询计划中使用的索引。

每当您询问有关性能的问题时,都应显示查询计划。要查看查询计划,请使用输入参数的变量在函数内部运行查询。从SSMS菜单中,启用“查询 - &gt;包含实际执行计划”选项

由于您没有足够的代表来发布图像,因此您可以按如下方式显示文本计划。在第一个SELECT语句中提供一些合理的参数。

set showplan_text on;

然后,在TEXT模式下运行以下内容,即按Ctrl-T然后按Ctrl-E。

DECLARE    
      @CustomerID INT
    , @StartDate DATETIME
    , @Days INT
    , @IncludeChildren BIT

SELECT 
      @CustomerID = 1
    , @StartDate = '20110201'
    , @Days = 10
    , @IncludeChildren = 1

DECLARE @TableOfValues TABLE 
(  
    RowID SMALLINT IDENTITY(1,1)
    , DailyLoadCount INT
    , DailyLoadDate VARCHAR(6)
    , FullDate DATETIME
) 

    DECLARE @MaxDate DATETIME;
    SET @MaxDate = DATEADD(dd, @Days * -1.7, DATEDIFF(dd, 0, @StartDate));

    WITH DateCTE AS
    (
        SELECT DATEADD(dd, 0, DATEDIFF(dd, 0, @StartDate)) AS DateValue
        UNION ALL
        SELECT DATEADD(DAY, -1, DateValue)
        FROM DateCTE
        WHERE DATEADD(DAY, -1, DateValue) > @MaxDate
    )
    INSERT INTO @TableOfValues
    SELECT * FROM
    (
        SELECT TOP (@Days)
            (
                SELECT COUNT(*)
                FROM dbo.[Load] l WITH (NOLOCK)
                JOIN dbo.LoadCustomer lc WITH (NOLOCK)
                    ON lc.LoadID = l.ID
                JOIN dbo.Customer c WITH (NOLOCK)
                    ON c.ID = lc.CustomerID
                WHERE DATEADD(dd, 0, DATEDIFF(dd, 0, l.LoadDate)) = dct.DateValue
                    AND l.StateType = 1
                    AND lc.Main = 1
                    AND (c.ID = @CustomerID OR (@IncludeChildren = 1 AND c.ParentCustomerID = @CustomerID))
            ) AS DailyLoadCount
            , CONVERT(VARCHAR(6), dct.DateValue, 107) AS DailyLoadDate
            , dct.DateValue
        FROM DateCTE dct
        WHERE
            DATEPART(DW, dct.DateValue) NOT IN (1, 7)
            AND dct.DateValue NOT IN
            (
                SELECT HolidayDate FROM Holiday
            )
        ORDER BY dct.DateValue DESC
    ) AS S
    ORDER BY s.DateValue ASC

SELECT * FROM @TableOfValues

将计划编入您的问题

答案 1 :(得分:1)

您应该使用内嵌UDF (现在您实际上正在使用临时表) 见http://msdn.microsoft.com/en-us/library/ms189294.aspx

或者将其转换为视图。

答案 2 :(得分:0)

相关子查询逐行运行,请勿使用它们。请改为使用连接或连接到派生表。您还需要确保where子句可以利用索引。搜索saragble查询以查看哪些类型的内容无法使用索引以及可以使用索引来执行哪些操作。