【发布时间】:2020-05-16 10:44:12
【问题描述】:
我有一张这样的桌子:
ItemID ItemFormula
100 'ID_3+ID_5'
110 'ID_2+ID_6'
120 'ID_100+ID_110'
130 'ID_120+ID_4'
这是一个公式表的简化版本,包含近 1000 条记录和多达 40 级参考(用于其他项目的项目)。任务是将公式分解为一个级别的参考,其中一个项目中没有其他项目。例如在上表中 id=130 我应该有 '((ID_3+ID_5)+(ID_2+ID_6))+ID_4'
编辑:操作不限于“+”,项目之间有一个可识别的字符。为了简单起见,我删除了那个字符。
我可以为此使用递归 CTE。但我的问题是,由于参考水平很高,我的递归选择有很多记录加入,所以需要很多时间才能完成。
我的问题是:我可以只在每次递归发生时保留之前的递归吗?
这是我的 CTE 代码
WITH Formula
AS (SELECT A.ItemID
,'ID_' + CONVERT(VARCHAR(20), A.ItemID) AS ItemText
,CONVERT(VARCHAR(MAX), A.ItemFormula) AS ItemFormula
FROM (VALUES (100,'ID_3+ID_5'),
(110,'ID_2+ID_6'),
(120,'ID_100+ID_110'),
(130,'ID_120+ID_4')
) A (ItemID,ItemFormula)
)
,REC
AS
(
SELECT A.ItemID
,A.ItemText
,A.ItemFormula
,1 AS LevelID
FROM Formula A
UNION ALL
SELECT A.ItemID
,A.ItemText
,' '
+ TRIM (REPLACE (REPLACE (A.ItemFormula, B.ItemText, ' ( ' + B.ItemFormula + ' ) '), ' ', ' '))
+ ' ' AS ItemFormula
,A.LevelID + 1 AS LevelID
FROM REC A
CROSS APPLY
(
SELECT *
FROM
(
SELECT *
,ROW_NUMBER () OVER (ORDER BY GETDATE ()) AS RowNum
FROM Formula B2
WHERE CHARINDEX (B2.ItemText, A.ItemFormula) > 0
) B3
WHERE B3.RowNum = 1
) B
)
,FinalQ
AS
(
SELECT A2.ItemID
,A2.ItemFormula
,A2.LevelID
FROM
(
SELECT A.ItemID
,REPLACE (TRIM (A.ItemFormula), ' ', '') AS ItemFormula
,A.LevelID
,ROW_NUMBER () OVER (PARTITION BY A.ItemID ORDER BY A.LevelID DESC) AS RowNum
FROM REC A
) A2
WHERE A2.RowNum = 1
)
SELECT * FROM FinalQ A2 ORDER BY A2.ItemID;
提前致谢。
【问题讨论】:
-
用您正在使用的数据库标记您的问题。
+是唯一允许的操作吗?我们可以为引用假设一个更好的格式,例如[ID_1]? -
ROW_NUMBER () OVER (ORDER BY GETDATE ())- 这将不确定地对行进行排序,因为GETDATE()将为整个语句运行一次。根据您需要做的事情,您可能会更幸运地使用存储过程执行此操作,或者将其拉入常规应用程序并构建依赖关系图。或者重新设计设计,这样您就不必每次都解析列表,只需解析一个 id。 -
@Clockwork-Muse 是为了保证连接只为引用多个项目的项目返回一行。这样公式中的项目就会被一一处理。因为 ROW_NUMBER 必须有一个 ORDER BY 子句,所以我把它放在那里。顺序在这里无关紧要。
-
@GordonLinoff 我在我发布的 EDIT 中回答了你的问题。
标签: sql sql-server recursion replace