【发布时间】:2014-02-25 00:33:37
【问题描述】:
我在 SQL Server 2012 中有一个表,其中包含如下自联接数据:
ID | ChildA_ID | ChildB_ID
(int) | (int) | (int)
-----------------------------
1 | |
2 | 1 |
3 | 1 |
4 | 3 |
6 | 2 | 4
7 | 6 |
使用 T-SQL(或者,在紧要关头,CLR 代码),我想把它变成如下所示的 XML:
<row>
<ID>7</ID>
<ChildA>
<row>
<ID>6</ID>
<ChildA>
<row>
<ID>2</ID>
<ChildA>
<row>
<ID>1</ID>
</row>
</ChildA>
</row>
</ChildA>
<ChildB>
<row>
<ID>4</ID>
<ChildA>
<row>
<ID>3</ID>
<ChildA>
<row>
<ID>1</ID>
</row>
</ChildA>
</row>
</ChildA>
</row>
</ChildB>
</row>
</ChildA>
</row>
我在 SQL Server 中的递归 CTE 和 FOR XML 子句方面有相当多的经验,但是这个让我非常困惑。 (我不排除我只是在拉哑巴的可能性。)
欢迎提出任何建议,包括构建数据以达到预期结果的替代方法。 (但请注意,“ChildA”和“ChildB”代表不同的类型父子关系,而不是非规范化。)
编辑:
在使用我的示例数据尝试 Mitch 的解决方案时,我遇到了一些重复的行:
<row>
<ID>7</ID>
<ChildA>
<row>
<ID>6</ID>
<ChildA>
<row>
<ID>2</ID>
<ChildA>
<row>
<ID>1</ID>
</row>
</ChildA>
</row>
<!--This is the first duplicate row:-->
<row>
<ID>2</ID>
<ChildA>
<row>
<ID>1</ID>
</row>
</ChildA>
</row>
</ChildA>
<ChildB>
<row>
<ID>4</ID>
<ChildA>
<!--This is the second "duplicate" row:-->
<row>
<ID>3</ID>
</row>
<row>
<ID>3</ID>
<ChildA>
<row>
<ID>1</ID>
</row>
</ChildA>
</row>
</ChildA>
</row>
</ChildB>
</row>
</ChildA>
</row>
编辑 2:
这是我用来生成上述结果的代码(米奇解决方案的略微修改版本):
IF NOT EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'Nodes')
CREATE TABLE Nodes (id int, leftid int, rightid int);
DELETE FROM Nodes
INSERT INTO Nodes
VALUES (1, null, null),
(2, 1, null),
(3, 1, null),
(4, 3, null),
(6, 2, 4),
(7, 6, null);
CREATE TABLE #xml (id int, depth int, data xml);
DECLARE @rootNode int = 7;
WITH cte (cid, depth, child) as
(
-- anchor
SELECT id, 1, leftid
FROM nodes
WHERE id = @rootNode and leftid is not null
UNION ALL
SELECT id, 1, rightid
FROM nodes
WHERE id = @rootNode and rightid is not null
UNION ALL
-- recursive
SELECT n.id, depth + 1, leftid
FROM cte c
INNER JOIN nodes n on c.child = n.id
WHERE leftid is not null or (rightid is null and leftid is null)
UNION ALL
SELECT n.id, depth + 1, rightid
FROM cte c
INNER JOIN nodes n on c.child = n.id
WHERE rightid is not null
)
INSERT INTO #xml (id, depth, data)
SELECT DISTINCT cid, depth, null
FROM CTE;
DECLARE @maxDepth int;
SET @maxDepth = (SELECT MAX(depth) FROM #xml)
WHILE @MaxDepth > 0
BEGIN
UPDATE xu
SET data = (
SELECT n.ID, xl.data as ChildA, xr.data as ChildB
FROM Nodes n
LEFT OUTER JOIN #xml xl on n.leftid = xl.id
LEFT OUTER JOIN #xml xr on n.rightid = xr.id
WHERE xu.id = n.id
FOR XML PATH('row'), TYPE
)
FROM #xml xu
WHERE xu.Depth = @MaxDepth
SET @MaxDepth = @MaxDepth - 1
END
SELECT data FROM #xml WHERE id = @RootNode
DROP TABLE #xml
【问题讨论】:
标签: sql-server xml recursive-query