gpt4 book ai didi

sql - TSQL - 递归 CTE 效率低下 - 需要替代方案

转载 作者:行者123 更新时间:2023-12-02 23:42:33 25 4
gpt4 key购买 nike

这是包含示例数据的表格:

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 - 相关 ItemID 的所有后续行上的 A

以下是我期望的结果:

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 个 ItemId,每个重复 10 次。正如预期的那样,它立即完成了所有第一行计算,然后计算时间急剧增加。

如您所见,我尝试了 INNER JOINCROSS JOIN。我相信它们具有与我为CROSS JOIN提供的参数相同的执行计划。

是否有更有效/高效的方法来实现这一目标?

昨天我让它运行了 5 个小时,看看它是否结束了……它没有结束。

另一个注意事项:当我在测试数据上使用它时,我SELECT而不使用ORDER希望有助于加快速度。 ORDER 只是为了我在事实检查时方便。

最佳答案

您的问题是您正在使用 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

在查询的查询计划中,问题显示在右下角的表扫描中。使用此测试数据,执行 8 次,总共返回 64 行:

enter image description here

使用临时表进行查询的查询计划: enter image description here enter image description here

关于sql - TSQL - 递归 CTE 效率低下 - 需要替代方案,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12827236/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com