如何在SQL中创建后序遍历?
在数据库中有一个树结构(id,parentId,order)其中order意味着每个父级排序的值(它意味着父级列表中的顺序),如何构建完整的SQL查询来按后序遍历此表 什么是post-order在wiki上有描述,尽管只针对二叉树- 并不是所有这些都适用于自定义树(例如按顺序),但实际上可以应用后顺序:如何在SQL中创建后序遍历?,sql,sql-server,tsql,Sql,Sql Server,Tsql,在数据库中有一个树结构(id,parentId,order)其中order意味着每个父级排序的值(它意味着父级列表中的顺序),如何构建完整的SQL查询来按后序遍历此表 什么是post-order在wiki上有描述,尽管只针对二叉树- 并不是所有这些都适用于自定义树(例如按顺序),但实际上可以应用后顺序: A / \ B C /|\ D E F 输出将是: B D E F C A 在SQL数据表中也是如此: |Id |ParentId
A
/ \
B C
/|\
D E F
输出将是:
B D E F C A
在SQL数据表中也是如此:
|Id |ParentId | Order
|___|_________|______
|A |null |0
|B |A |0
|C |A |1
|D |C |0
|E |C |1
|F |C |2
我一直在努力解决这个问题,但CTE似乎不允许内部ORDER BY子句(omg,为什么?!),因此在我当前的级别上,如果没有存储过程,这个任务就不可能完成。通过简单地按原样实现它,找到了解决方案:
If(OBJECT_ID('tempdb..#result') Is Not Null) Drop Table #result;
If(OBJECT_ID('tempdb..#stack') Is Not Null) Drop Table #stack;
create table #result (id int not null identity primary key, [value] bigint null);
create table #stack (id int not null identity primary key, [value] bigint null);
INSERT INTO #stack values(null) --inserting root identifiers here
WHILE EXISTS (SELECT * FROM #stack)
BEGIN
declare @stack_id int, @stack_value bigint
select top 1 @stack_id=id, @stack_value=value from #stack order by id desc
delete from #stack where id=@stack_id
INSERT INTO #stack
-- here comes our query, which should fetch children of specified id and order it
select tos.id
from inputTable as t
where (ISNULL(@stack_value, 0) = 0 AND t.ParentId IS NULL) OR (ISNULL(@stack_value, 0) != 0 AND t.ParentId = @stack_value)
order by t.[order] asc
insert into #result values(@stack_value)
END
select [value] from #result order by id desc
If(OBJECT_ID('tempdb..#result') Is Not Null) Drop Table #result;
If(OBJECT_ID('tempdb..#stack') Is Not Null) Drop Table #stack;
到目前为止,使用CTE似乎是不可能的。这是一个基于CTE的版本,与其说是一个可用的答案,不如说是一个概念证明。它使用
STRING\u AGG
按顺序连接每个节点的子节点,然后递归地将每个节点替换为其子节点以构建输出字符串-这意味着它在节点键是彼此的子字符串的情况下不起作用
DECLARE @t TABLE
(id CHAR(1) NOT NULL PRIMARY KEY,
parentid CHAR(1) NULL,
roworder TINYINT NOT NULL
)
INSERT @t (id, parentid, roworder)
VALUES('A', NULL, 0),
('B','A',0),
('C','A',1),
('D','C',0),
('E','C',1),
('F','C',2),
('G','E',0),-- two extra nodes to prove that this isn't a one-off
('H','E',1)
;WITH aggCTE
AS
(
SELECT parentid, STRING_AGG(CONVERT(VARCHAR(MAX), id), ' ') WITHIN GROUP (ORDER BY Roworder) AS children
FROM @t
GROUP BY parentid
)
,recCTE
AS
(
SELECT a.parentid,
a.children,
CAST(ISNULL(a.parentid,'') AS VARCHAR(MAX)) AS processed, --to prevent loops
0 AS seq, --to pick the right output row
a.children AS firstnode --to disambiguate if the data contains multiple trees
FROM aggCTE AS a
WHERE a.parentid IS NULL
UNION ALL
SELECT a.parentid,
REPLACE(a.children, b.parentid, CONCAT(b.children, ' ', b.parentid)) AS children,
CONCAT(a.processed, b.parentid) AS processed,
a.seq + 1 AS seq,
a.firstnode
FROM recCTE AS a
JOIN aggCTE AS b
ON CHARINDEX(b.parentid, a.children) > 0
AND CHARINDEX(b.parentid, a.processed) = 0
)
,rnCTE
AS
(
SELECT children,
ROW_NUMBER() OVER (PARTITION BY firstnode ORDER BY seq DESC) AS rn
FROM recCTE
)
SELECT children AS post_order_traversal
FROM rnCTE
WHERE rn = 1
我认为这是一个非常好的问题。遗憾的是,很多人会投票反对,因为他们想要一些东西1)你尝试了什么2)实验数据集3)根据我的经验,没有多少人不知道什么是
后序
树导航,所以你可能想解释一下,已经有很多关于堆栈溢出的例子了。解决方案围绕自联接和并集以及CTE(公共表表达式)展开。下面是Oracle中的一个解决方案。但基本前提是一样的。。尝试在谷歌上搜索“深度优先”或“广度优先”以获得更具体的解决方案,包括sqlserverDepth first有多个变体,其中一个是POST-ORDER(LRN),我没有找到。谷歌搜索只返回典型的预订单解决方案,但在sql server中,我不能在递归CTE中指定ORDER BY,所以不能以这种方式完成。太棒了!大多数时候Id都是“bigint”或“int”,所以我认为可以将键转换为固定大小的十六进制表示,以防止子字符串冲突!这是个好答案!我想说的是,它在足够大的桌子上也不起作用。即使指定了max,字符串也是有限的。