使用嵌套XML

时间:2016-03-30 12:09:15

标签: sql sql-server xml tsql

我有一个类似于以下结构的xml文件:

<root>
 <companies>
  <company>
   <name>XYZ Co. </name>
   <addresses>
    <address>
     <street>12 Light Street</street>
    </address>
    <address>
     <street>44 King Street</street>
     <contacts>
      <contact>
        <fullName>Bob</fullName>
      </contact>
      <contact>
        <fullName>Alice</fullName>
      </contact>
     </contacts>
    </address>
   </addresses>
  </company>
 </companies>
 ...
 ...
</root>

因此,可能会有许多公司 地址,并且在地址中可能会有很多联系人。现在我试图查询这个xml来插入相关的表(T-SQL)。 表格设计如下:

  • 公司(Id,Name),
  • 地址(AddressId,CompanyId,Street)
  • 联系人(ContactId,AddressId,FullName)

我看到过类似的问题:Bulk insert nested xml with foreign key as identity column of first table,但依赖其中一个父字段是唯一的。

我正在考虑沿着光标路走下去......希望有更好的选择。任何帮助将不胜感激。

非常感谢

1 个答案:

答案 0 :(得分:2)

编辑:使用生成的ID

此解决方案依赖于将XML加载到XML类型的变量@xml中。如果您需要帮助,请致电; - )

WITH MyCompanies AS
(
    SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS CompID
          ,Comp.value('name[1]','varchar(max)') AS Comp_Name
          ,Comp.query('.') AS Comp_InnerXML
    FROM @xml.nodes('/root/companies/company') AS A(Comp)
)
,TheAddresses AS
(
    SELECT MyCompanies.*
          ,ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS AddrID
          ,Addr.value('street[1]','varchar(max)') AS Addr_Street 
          ,Addr.query('.') AS Addr_InnerXML
    FROM MyCompanies
    OUTER APPLY Comp_InnerXML.nodes('company/addresses/address') AS A(Addr) 
)
SELECT TheAddresses.*
      ,ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS ContID
      ,Cont.value('fullName[1]','varchar(max)') AS Cont_FullName 
INTO #temp
FROM TheAddresses
OUTER APPLY Addr_InnerXML.nodes('address/contacts/contact') AS C(Cont);

SELECT * FROM #temp;

结果

+--------+-----------+--------+-----------------+--------+---------------+
| CompID | Comp_Name | AddrID | Addr_Street     | ContID | Cont_FullName |
+--------+-----------+--------+-----------------+--------+---------------+
| 1      | XYZ Co.   | 1      | 12 Light Street | 1      | NULL          |
+--------+-----------+--------+-----------------+--------+---------------+
| 1      | XYZ Co.   | 2      | 44 King Street  | 2      | Bob           |
+--------+-----------+--------+-----------------+--------+---------------+
| 1      | XYZ Co.   | 2      | 44 King Street  | 3      | Alice         |
+--------+-----------+--------+-----------------+--------+---------------+
| 2      | ABC Co.   | 3      | 12 ABC Street   | 4      | NULL          |
+--------+-----------+--------+-----------------+--------+---------------+
| 2      | ABC Co.   | 4      | 44 ABC Street   | 5      | Bob ABC       |
+--------+-----------+--------+-----------------+--------+---------------+
| 2      | ABC Co.   | 4      | 44 ABC Street   | 6      | Alice ABC     |
+--------+-----------+--------+-----------------+--------+---------------+

在#temp中你有数据。使用DISTINCT从那里进行插入...

--Your companies
SELECT DISTINCT CompID,Comp_Name FROM #temp;
--Your address
SELECT DISTINCT CompID,AddrID,Addr_Street  FROM #temp;
--Your contacts
SELECT DISTINCT AddrID,ContID,Cont_FullName  FROM #temp;

此查询将数据转换为表格数据。

但是:您应该将其插入相关的表格(公司,地址,联系人),而不是 all in one

您可以使用带有ROW_NUMBER的{​​{1}}来创建虚拟ID,以生成虚拟ID,但这仅适用于第一次运行。

OVER(PARTITION BY ...)

结果

SELECT Comp.value('name[1]','varchar(max)') AS Comp_Name
      ,Addr.value('street[1]','varchar(max)') AS Addr_Street 
      ,Cont.value('fullName[1]','varchar(max)') AS Cont_FullName 
FROM @xml.nodes('/root/companies/company') AS A(Comp)
OUTER APPLY Comp.nodes('addresses/address') AS B(Addr) 
OUTER APPLY Addr.nodes('contacts/contact') AS C(Cont)