2011-02-24 82 views
1

我目前工作中,我使用递归CTE的功能,但似乎都表现不佳。我需要这个功能(所以没有临时表),所以我可以很容易地在存储过程中使用它。功能的递归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秒,这将被称为超过数千)。

+0

我不认为这个问题是与你的递归表(它应该是相当快),但对你的'SELECT' – Lamak 2011-02-24 17:54:11

回答

3

你的主要问题是,就在这里

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

应该

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

创建上载合成指数法(LoadDate,ID)和加载(ID,LoadDate)和下降,做了一个没有在查询计划中使用。

您应该显示查询计划,只要你是问有关的性能问题。要查看查询计划,请使用变量为输入参数自行在函数内运行查询。从SSMS菜单,启用选项“查询 - >包括实际的执行计划”

既然你没有足够的代表,发布图片,您可以按如下揭示文本计划。在第一个SELECT语句中提供一些合理的参数。

set showplan_text on; 

然后,在文本模式下,即,按下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 

编辑计划到你的问题

+0

子查询什么是包括在这里查询计划的最佳方式?我尝试了你的建议,但要么没有改善,要么看起来很少。 – Manny 2011-02-24 18:08:54

+0

@曼尼 - 请参阅答案 – RichardTheKiwi 2011-02-24 18:19:46

+0

中的步骤我对这个评论有点草率。我没有注意到你写的关于综合指数的内容。在添加该索引后,我看到了巨大的改进。我以为我已经自己添加了它,但看起来像我错过了它。谢谢! – Manny 2011-02-24 19:40:45

1

您应该使用内联UDF代替(现在使用的是实际上是一个临时表) 见http://msdn.microsoft.com/en-us/library/ms189294.aspx

或将其转换成图代替。

+0

我说没有临时表,因为大多数人试图通过用日期填充临时表然后为它进行外连接来解决这个问题,等等。 – Manny 2011-02-24 19:38:37

0

相关子查询运行一行接一行,不使用它们。相反,使用连接或连接到派生表。您还需要确保where子句可以利用索引。在可疑的查询上搜索,看看哪些东西不能使用索引,以及如何使索引使用索引。