我有一个表S
,其中包含2列:Name
用于ID和XML列。
此表表示树数据。
S
-----+----------------
Name | XmlCol
-----+----------------
A | <E><B Id='b1' Type=0 /><B Id='D' Type=1 /><B Id='b2' Type=0 /></E>
D | <E><B Id='b3' Type=0 /><B Id='G' Type=1 /></E>
F | <E><B Id='b4' Type=0 /></E>
G | <E><B Id='b5' Type=0 /></E>
数据按给定顺序显示。 此处的订单很重要。
注意XML结构。
Type = 0表示该条目的类型为leaf
Type = 1表示表中有一行具有相同的Name
,因此是节点而不是叶子。
有5片叶子,b1,b2,b3,b4,b5。
我想要的是获得所有序列中所有叶子的表,如下所示:
Leaves
--------
b1
b3
b5
b2
b4
当起始节点为'A'
这是XML解析片段,但它只是开始。
SELECT [Id] = xTree.b.value('@Id', 'varchar(10)')
FROM [S]
CROSS APPLY [XmlCol].nodes('/E/B') AS xTree(b)
有人可以建议如何在SQL中执行此操作吗?
答案 0 :(得分:1)
尝试这样的事情:
DECLARE @Source TABLE (
Name VARCHAR(10) PRIMARY KEY,
XmlCol XML NOT NULL
)
DECLARE @Root VARCHAR(10)='A'
INSERT INTO @Source VALUES
('A',CONVERT(XML,'<E><B Id="b1" Type="0" /><B Id="D" Type="1" /><B Id="b2" Type="0" /></E>')),
('D',N'<E><B Id="b3" Type="0" /><B Id="G" Type="1" /></E>'),
('F',N'<E><B Id="b4" Type="0" /></E>'),
('G',N'<E><B Id="b5" Type="0" /></E>')
DECLARE @Temp1 TABLE (
ID INT IDENTITY PRIMARY KEY,
Name VARCHAR(10) NOT NULL,
Type INT NOT NULL,
Value VARCHAR(10) NOT NULL
)
INSERT INTO @Temp1
SELECT Name, xTree.b.value('@Type', 'int') AS Type, xTree.b.value('@Id', 'varchar(10)') AS Value
FROM @Source
CROSS APPLY [XmlCol].nodes('/E/B') AS xTree(b)
DECLARE @Temp2 TABLE (
ID INT PRIMARY KEY,
Name VARCHAR(10) NOT NULL,
Type INT NOT NULL,
Value VARCHAR(10) NOT NULL,
Position INT NOT NULL
)
INSERT INTO @Temp2
SELECT *, ROW_NUMBER() OVER (PARTITION BY Name ORDER BY ID) AS Position
FROM @Temp1
DECLARE @Temp3 TABLE (
Name VARCHAR(10) NOT NULL,
Type INT NOT NULL,
Value VARCHAR(10) NOT NULL,
Position FLOAT NOT NULL,
Level INT NOT NULL,
PRIMARY KEY (Name, Position)
)
;WITH CTE AS (
SELECT Name, Type, Value, CONVERT(FLOAT,Position) AS Position, 0 AS Level
FROM @Temp2 WHERE Type=0
UNION ALL
SELECT t1.Name, t2.Type, t2.Value,
t1.Position+t2.Position*POWER(CONVERT(FLOAT,0.1),1+t2.Level),
t2.Level+1 AS Level
FROM @Temp2 t1
INNER JOIN CTE t2 ON t2.Name=t1.Value
WHERE t1.Type=1
)
INSERT INTO @Temp3
SELECT * FROM CTE
SELECT Value
FROM (
SELECT Value, 0 AS Extra, Position
FROM @Temp3 WHERE Name=@Root
UNION ALL
SELECT Value, 1 AS Extra, Position
FROM @Temp3 WHERE Value NOT IN (
SELECT Value
FROM @Temp3 WHERE Name=@Root
)
) u
ORDER BY Extra, Position
一些观察结果:
在XML中,应始终引用属性的值
不清楚你想要哪个顺序在给定根的树之外的值(在这个例子中,只有b4不是从A开始的树的一部分;如果有多个像这样的值,在其他多棵树中,不清楚哪个是所需的顺序)
使用更复杂的CTE可以避免使用表变量,但我认为它们有助于提高性能
我假设每个级别最多有10个子节点;如果有更多的子节点,您可以将0.1更改为0.01,例如