这是一个包含样本数据的表格:
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个ItemId,每个重复10次。它按预期完成所有第一行计算,然后计算时间逐渐增加。
正如您所看到的,我已经尝试了INNER JOIN
和CROSS JOIN
。我相信它们与我给出CROSS JOIN
的参数具有相同的执行计划。
有没有更有效/更有效的方法来实现这一目标?
我昨天允许它运行5个小时以查看它是否已经结束..它没有。
另一个注意事项:当我在测试数据SELECT
上使用它时,不使用ORDER
来帮助加快速度。当我正在检查时,ORDER
只是为了方便我。
答案 0 :(得分: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
在查询的查询计划中,问题显示在右下角的表扫描中。使用此测试数据,它将执行8次,总共返回64行:
查询使用临时表计划查询:
答案 1 :(得分: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