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