2

我有一个S有 2 列的表:NameID 列和 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 表示条目是叶子类型。
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 中执行此操作?

4

1 回答 1

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,比如

于 2013-06-20T19:58:24.427 回答