Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/sql-server-2005/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql 带有递归CTE的函数似乎非常慢_Sql_Sql Server 2005_Tsql_Common Table Expression - Fatal编程技术网

Sql 带有递归CTE的函数似乎非常慢

Sql 带有递归CTE的函数似乎非常慢,sql,sql-server-2005,tsql,common-table-expression,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 (

我目前正在开发一个使用递归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应该检索的是过去@天(无周末/假日)内每天的加载数


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

您的主要问题就在这里

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

WHERE l.LoadDate >= dct.DateValue
  AND l.LoadDate <  dct.DateValue +1
然后,在文本模式下运行以下命令,即按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

将计划编辑到您的问题中

您的主要问题就在这里

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

WHERE l.LoadDate >= dct.DateValue
  AND l.LoadDate <  dct.DateValue +1
然后,在文本模式下运行以下命令,即按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

将计划编辑到您的问题中

您应该使用内联自定义项(现在您实际使用的是临时表) 看


或者将其转换为视图。

您应该使用内联UDF(现在您实际使用的是临时表) 看


或者将其转换为视图。

相关子查询逐行运行,不要使用它们。改为使用联接或派生表的联接。您还需要确保任何where子句都可以利用索引。搜索saragble查询,查看哪些内容不能使用索引,以及如何使其使用索引。

相关子查询逐行运行,不要使用它们。改为使用联接或派生表的联接。您还需要确保任何where子句都可以利用索引。搜索saragble查询,查看哪些内容不能使用索引,以及可以采取哪些措施使其使用索引。

我不认为问题出在递归表上(它应该非常快),但对于
SELECT上的子查询
我不认为问题出在递归表上(它应该非常快),但是在
选择
的子查询中,在这里包含查询计划的最佳方式是什么?我试过你的建议,但不是没有改进,就是看起来很小。@Manny-请看回答中的步骤。我对这个评论有点草率。我没有注意到你写的关于综合指数的内容。在加入这个指数后,我看到了巨大的进步。我以为我自己已经加上了,但看起来我错过了。谢谢在这里包含查询计划的最佳方式是什么?我试过你的建议,但不是没有改进,就是看起来很小。@Manny-请看回答中的步骤。我对这个评论有点草率。我没有注意到你写的关于综合指数的内容。在加入这个指数后,我看到了巨大的进步。我以为我自己已经加上了,但看起来我错过了。谢谢我说没有临时表是因为大多数人试图通过用日期填充临时表,然后对其进行外部联接来解决这个问题。我说没有临时表是因为大多数人试图用日期填充临时表,然后对其进行外部联接来解决这个问题,等等。