Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/delphi/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何在SQL中创建后序遍历?_Sql_Sql Server_Tsql - Fatal编程技术网

如何在SQL中创建后序遍历?

如何在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

在数据库中有一个树结构(id,parentId,order)其中order意味着每个父级排序的值(它意味着父级列表中的顺序),如何构建完整的SQL查询来按后序遍历此表

什么是post-order在wiki上有描述,尽管只针对二叉树-

并不是所有这些都适用于自定义树(例如按顺序),但实际上可以应用后顺序:

      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,字符串也是有限的。