4

這裏是樣本數據的表:TSQL - 遞歸CTE低效 - 需要一個替代

DECLARE @TestTable TABLE (
    ItemID INT, 
    A INT, 
    B INT, 
    Month INT) 

INSERT INTO @TestTable VALUES (1234, 5, 9, 1) 
INSERT INTO @TestTable VALUES (1234, 6, 9, 2) 
INSERT INTO @TestTable VALUES (4321, 5, 11, 1) 
INSERT INTO @TestTable VALUES (4321, 12, 11, 2) 
INSERT INTO @TestTable VALUES (1324, 14, 6, 1) 
INSERT INTO @TestTable VALUES (1324, 5, 6, 2) 
INSERT INTO @TestTable VALUES (1234, 1, 9, 3) 
INSERT INTO @TestTable VALUES (1324, 9, 6, 3) 

一些需要注意的是,B列總是相同的,因爲它是在這個計算中只使用一次,但初始計算需要。

我試圖在第一行中從A中減去B,然後在後續的行上減去前面的A行的差異。有效地,B - A = C上的第一個,然後C - A對於相關ItemID的所有後續行。

這裏是我期待的結果:

ItemID A B C Month RowNumber 
1234 5 9 4 1  1 
1234 6 9 -2 2  2 
1234 1 9 -3 3  3 
1324 14 6 -8 1  1 
1324 5 6 -13 2  2 
1324 9 6 -22 3  3 
4321 5 11 6 1  1 
4321 12 11 -6 2  2 

這裏是我如何實現這一點。

;WITH CTE_TestValue AS (
    SELECT 
     Main.ItemID, 
     Main.A, 
     Main.B, 
     Main.Month, 
     ROW_NUMBER() OVER (Partition BY Main.ItemID ORDER BY Main.Month) AS RowNumber 
    FROM @TestTable AS Main 
), 
CTE_TestColumnC AS (
    SELECT 
     MainA.ItemID, 
     MainA.A, 
     MainA.B, 
     (MainA.B - MainA.A) AS C, 
     MainA.Month, 
     MainA.RowNumber 
    FROM CTE_TestValue AS MainA 
     WHERE MainA.Rownumber = 1 

    UNION ALL 

    SELECT 
     MainB.ItemID, 
     MainB.A, 
     MainB.B, 
     (Sub.C - MainB.A) AS C, 
     MainB.Month, 
     MainB.RowNumber 
    FROM CTE_TestValue AS MainB 
     INNER JOIN CTE_TestColumnC AS Sub 
      ON MainB.RowNumber - 1 = Sub.RowNumber 
      AND MainB.ItemID = Sub.ItemID 
--  CROSS JOIN CTE_TestColumnC AS Sub 
--   WHERE Sub.RowNumber + 1 = MainB.RowNumber 
--   AND MainB.ItemID = Sub.ItemID 
) 
SELECT 
    Main.ItemID, 
    Main.A, 
    Main.B, 
    Main.C, 
    Main.Month, 
    Main.RowNumber 
FROM CTE_TestColumnC AS Main 
ORDER BY ItemID, Month, RowNumber 

能正常工作的一個小數據樣本,但我處理約20,000項目Id的每個重複10次。如預期的那樣,它立即完成所有第一排計算,然後計算時間以DRASTICALLY遞增。

正如你所見,我已經嘗試了INNER JOINCROSS JOIN。我相信他們與我給出的參數CROSS JOIN具有相同的執行計劃。

是否有更有效/更高效的方法來完成此操作?

我讓它在昨天運行了5個小時,看它是否結束..它沒有。

另一個注意事項:當我在測試數據上使用I SELECT而不使用ORDER希望有助於加快速度。 ORDER只是爲了我的方便,當我實際檢查。

+0

很肯定這是不確定的BY Main.ItemID作爲ItemID重複的Main.ItemID。 – Paparazzi

+0

這真是一個更大的問題,一個沉悶的例子。實際上,我正在使用的數據中有另一列,將根據需要正確命令它。我只是不想渾濁的例子,因爲這使我在過去沒有答案 – jayEss

+0

基於我的示例查詢的最高票數我假設這是最好的方式。我想我需要找到一種方法來索引數據樣本,希望能夠加快速度。任何人都可以解釋爲什麼這個計算需要這麼久嗎?我假設它與遞歸「循環」有關,它必須重新選擇每個ItemID的前一個數據集10次。 – jayEss

回答

5

你的問題是,您使用的是CTE爲遞歸CTE的來源。您的第一個CTE將在遞歸CTE的每次迭代中執行一次。使用您的測試數據,這意味着CTE_TestValue創建了8次。

放的CTE_TestValue結果在具有上(RowNumber, ItemID)聚集主鍵一個臨時表,並使用該臨時表,作爲遞歸CTE CTE_TestColumnC數據的來源。

還將遞歸部分中的連接條件更改爲ON MainB.RowNumber = Sub.RowNumber + 1。這將使查詢能夠使用臨時表上的索引。

DECLARE @TestTable TABLE (
    ItemID INT, 
    A INT, 
    B INT, 
    Month INT) 

INSERT INTO @TestTable VALUES (1234, 5, 9, 1) 
INSERT INTO @TestTable VALUES (1234, 6, 9, 2) 
INSERT INTO @TestTable VALUES (4321, 5, 11, 1) 
INSERT INTO @TestTable VALUES (4321, 12, 11, 2) 
INSERT INTO @TestTable VALUES (1324, 14, 6, 1) 
INSERT INTO @TestTable VALUES (1324, 5, 6, 2) 
INSERT INTO @TestTable VALUES (1234, 1, 9, 3) 
INSERT INTO @TestTable VALUES (1324, 9, 6, 3) 

CREATE TABLE #TestValue 
(
    ItemID INT, 
    A INT, 
    B INT, 
    Month INT, 
    RowNumber INT, 
    primary key(RowNumber, ItemID) 
) 

INSERT INTO #TestValue 
SELECT 
    Main.ItemID, 
    Main.A, 
    Main.B, 
    Main.Month, 
    ROW_NUMBER() OVER (Partition BY Main.ItemID ORDER BY Main.Month) AS RowNumber 
FROM @TestTable AS Main 


;WITH CTE_TestColumnC AS (
    SELECT 
     MainA.ItemID, 
     MainA.A, 
     MainA.B, 
     (MainA.B - MainA.A) AS C, 
     MainA.Month, 
     MainA.RowNumber 
    FROM #TestValue AS MainA 
     WHERE MainA.Rownumber = 1 

    UNION ALL 

    SELECT 
     MainB.ItemID, 
     MainB.A, 
     MainB.B, 
     (Sub.C - MainB.A) AS C, 
     MainB.Month, 
     MainB.RowNumber 
    FROM #TestValue AS MainB 
     INNER JOIN CTE_TestColumnC AS Sub 
      ON MainB.RowNumber = Sub.RowNumber + 1 
      AND MainB.ItemID = Sub.ItemID 
) 
SELECT 
    Main.ItemID, 
    Main.A, 
    Main.B, 
    Main.C, 
    Main.Month, 
    Main.RowNumber 
FROM CTE_TestColumnC AS Main 
ORDER BY ItemID, Month, RowNumber 

DROP TABLE #TestValue 

在查詢的查詢計劃中,問題顯示在右下角的表掃描中。與此測試數據,它共有64行返回執行8次:

enter image description here

用於查詢的查詢計劃使用臨時表: enter image description here enter image description here

+0

謝謝一堆!奇蹟般有效! – jayEss

0

我希望我能正確理解你所要做的事情。
這裏是我的解決方案:

WITH DATA AS (
SELECT *, row_number() over (ORDER BY itemid) RN 
FROM TestTable), 
RECURSIVE AS (
    SELECT itemID, B-A AS C, RN 
    FROM DATA 
    WHERE RN = 1 
    UNION ALL 
    SELECT T1.itemID, t2.C - t1.A, t1.RN 
    FROM DATA AS T1 
    INNER JOIN 
    RECURSIVE AS T2 
    ON t1.RN = T2.Rn+1) 
SELECT ItemID, C 
FROM RECURSIVE 

你可以找到完整的例子(與您的數據)here

+0

這基本上與我有同樣的查詢,但你的不分區row_number,所以它沒有考慮到不同的數字與不同的項目有關。我應該在我的問題中指出這一點。 – jayEss