将xml递归地分解到数据库中

Fre*_*and 5 xml sql sql-server xquery xquery-sql

我有以下XML数据和Element表.

DECLARE @input XML = '<root>
     <C1>
       <C2>
         <C3>           <C4>data1</C4>       </C3>         
       </C2>
       <C2>
         <C3>data2</C3>
       </C2>
     </C1>
     <D1>
        <D2>data3</D2>
        <D2>data4</D2>
     </D1>
    </root>'
Run Code Online (Sandbox Code Playgroud)

元素表:(这只是一个例子,因此可以更改以匹配适当的解决方案.)

CREATE TABLE Element (  elementId INT IDENTITY PRIMARY KEY, 
elementName VARCHAR (200) NOT NULL, 
parentId INT,   
data VARCHAR(300) );
Run Code Online (Sandbox Code Playgroud)

根据@input,根元素是C1和D1的父元素,那么C1是C2父元素,...

SQL Server 2012/2014使用CTE(或任何其他类型的SQL对象)对存储过程进行编码以递归方式将所有元素名称放入Element表中的解决方案是什么?

在这种情况下,数据列填充数据,C4和第二C3以及D2元素具有其余元素为空的数据.

我也看到了Hierarchical Data类型,我想知道这是否有助于解决这个问题?

Mik*_*son 8

使用OpenXML,您可以使用metaproperties获取XML表的表示IDParentID列.

合并中使用XML查询将允许您在XML中创建elementId标识列和DOM节点标识之间的映射表.

最后一步是使用映射表来更新parentIdElement.

SQL小提琴

MS SQL Server 2008架构设置:

CREATE TABLE Element (  elementId INT IDENTITY PRIMARY KEY, 
elementName VARCHAR (200) NOT NULL, 
parentId INT,   
data VARCHAR(300) );
Run Code Online (Sandbox Code Playgroud)

查询1:

declare @input xml = '
<root>
  <C1>
    <C2>
      <C3>
        <C4>data1</C4>
      </C3>
    </C2>
    <C2>
      <C3>data2</C3>
    </C2>
  </C1>
  <D1>
    <D2>data3</D2>
    <D2>data4</D2>
  </D1>
</root>';

-- OpenXML handle
declare @D int;

-- Table that capture output of merge with mapping between 
-- DOM node id and the identity column elementID in Element 
declare @T table
(
  ID int,
  ParentID int,
  ElementID int
);

-- Parse XML and get a handle
exec sp_xml_preparedocument @D output, @input;

-- Add rows to Element and fill the mapping table @T
merge into dbo.Element as E
using ( 
      select *
      from openxml(@D, '//*') with 
        (
          ID int '@mp:id',
          ParentID int '@mp:parentid',
          Data varchar(300) 'text()',
          ElementName varchar(200) '@mp:localname'
        )
      ) as S
on 0 = 1
when not matched by target then
  insert (elementName, data) values (S.ElementName, S.data)
output S.ID, S.ParentID, inserted.elementID into @T;

-- Update parentId in Elemet
update E
set parentId =  T2.ElementID
from dbo.Element as E
  inner join @T as T1
    on E.elementId = T1.ElementID
  inner join @T as T2
    on T1.ParentID = T2.ID


-- Relase the XML document
exec sp_xml_removedocument @D;

select *
from Element;
Run Code Online (Sandbox Code Playgroud)

结果:

| ELEMENTID | ELEMENTNAME | PARENTID |   DATA |
|-----------|-------------|----------|--------|
|         1 |        root |   (null) | (null) |
|         2 |          C1 |        1 | (null) |
|         3 |          C2 |        2 | (null) |
|         4 |          C3 |        3 | (null) |
|         5 |          C4 |        4 |  data1 |
|         6 |          C2 |        2 | (null) |
|         7 |          C3 |        6 |  data2 |
|         8 |          D1 |        1 | (null) |
|         9 |          D2 |        8 |  data3 |
|        10 |          D2 |        8 |  data4 |
Run Code Online (Sandbox Code Playgroud)