2011-02-24 81 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子句可以利用索引。在可疑的查詢上搜索,看看哪些東西不能使用索引,以及如何使索引使用索引。