如何从具有动态节点数的XML文件创建SQL表?

时间:2022-10-31 13:07:25

I'm using SQL Server 2008.
The task: take an XML file and parse it into a(n) SQL table.
The problem: The number of columns and their names will vary based on the XML.

我正在使用SQL Server 2008.任务:获取XML文件并将其解析为(n)SQL表。问题:列数及其名称将根据XML而有所不同。

Here's some code:

这是一些代码:

DECLARE @xmlData XML;  
SET @xmlData = '<root>
  <item id="1">
    <item_number>IT23</item_number>
    <title>Item number twenty-three</title>
    <setting>5 to 20</setting>
    <parameter>10 to 16</parameter>
  </item>
  <item id="2">
    <item_number>RJ12</item_number>
     <title>Another item with a 12</title>
     <setting>7 to 35</setting>
     <parameter>1 to 34</parameter>
  </item>
  <item id="3">
     <item_number>LN90</item_number>
     <title>LN with 90</title>
     <setting>3 to 35</setting>
     <parameter>9 to 50</parameter>
  </item>
</root>'

For example using the above XML, I would need a SQL table returned that would look like this:

例如,使用上面的XML,我需要返回一个如下所示的SQL表:

如何从具有动态节点数的XML文件创建SQL表?

Here's how I got the above table:

以下是我如何得到上表:

   DECLARE @idoc INT;
   EXEC sp_xml_preparedocument @idoc OUTPUT, @xmlData

   SELECT * 
   FROM  OPENXML (@idoc, '/root/item', 2)
   WITH (item_number VARCHAR(100),
         title VARCHAR(100),
         setting VARCHAR(100),
         parameter VARCHAR(100))

Now let's say the XML changed where each item node had a new child node with the name 'new_node'. Like so:

现在让我们说XML改变了每个项目节点都有一个名为“new_node”的新子节点。像这样:

<root>
  <item id="1">
    <item_number>IT23</item_number>
    <title>Item number twenty-three</title>
    <setting>5 to 20</setting>
    <parameter>10 to 16</parameter>
    <new_node>data</new_node>
  </item>
  <item id="2">
    <item_number>RJ12</item_number>
    <title>Another item with a 12</title>
    <setting>7 to 35</setting>
    <parameter>1 to 34</parameter>
    <new_node>goes</new_node>
  </item>
  <item id="3">
    <item_number>LN90</item_number>
    <title>LN with 90</title>
    <setting>3 to 35</setting>
    <parameter>9 to 50</parameter>
    <new_node>here</new_node>
  </item>
</root>

I must change my code to include the new node:

我必须更改我的代码以包含新节点:

   SELECT * 
   FROM  OPENXML (@idoc, '/root/item', 2)
   WITH (item_number VARCHAR(100),
         title VARCHAR(100),
         setting VARCHAR(100),
         parameter VARCHAR(100),
         new_node VARCHAR(100))

To get this table:

要获得此表:

如何从具有动态节点数的XML文件创建SQL表?

So the problem is that the child nodes of 'item' will vary.

所以问题是'item'的子节点会有所不同。

How can I generate the same tables without specifying the columns? Is there some other approach than having to use OPENXML?

如何在不指定列的情况下生成相同的表?除了必须使用OPENXML之外还有其他方法吗?

2 个解决方案

#1


11  

With a dynamic number of columns you need dynamic SQL.

使用动态数量的列,您需要动态SQL。

declare @XML xml = 
'<root>
  <item id="1">
    <item_number>IT23</item_number>
    <title>Item number twenty-three</title>
    <setting>5 to 20</setting>
    <parameter>10 to 16</parameter>
    <new_node>data</new_node>
  </item>
  <item id="2">
    <item_number>RJ12</item_number>
    <title>Another item with a 12</title>
    <setting>7 to 35</setting>
    <parameter>1 to 34</parameter>
    <new_node>goes</new_node>
  </item>
  <item id="3">
    <item_number>LN90</item_number>
    <title>LN with 90</title>
    <setting>3 to 35</setting>
    <parameter>9 to 50</parameter>
    <new_node>here</new_node>
  </item>
</root>'

declare @SQL nvarchar(max) = ''
declare @Col nvarchar(max) = ', T.N.value(''[COLNAME][1]'', ''varchar(100)'') as [COLNAME]' 

select @SQL = @SQL + replace(@Col, '[COLNAME]', T.N.value('local-name(.)', 'sysname'))
from @XML.nodes('/root/item[1]/*') as T(N)

set @SQL = 'select '+stuff(@SQL, 1, 2, '')+' from @XML.nodes(''/root/item'') as T(N)' 

exec sp_executesql @SQL, N'@XML xml', @XML

#2


1  

improving on Mikael's answer:

改进Mikael的答案:

With a dynamic number of columns you need dynamic SQL. This code will dynamically build a select statement supporting an unknown number of nodes, including items with different node lists.

使用动态数量的列,您需要动态SQL。此代码将动态构建支持未知数量节点的select语句,包括具有不同节点列表的项目。

declare @XML xml = 
'<root>
  <item id="1">
    <item_number>IT23</item_number>
    <title>Item number twenty-three</title>
    <setting>5 to 20</setting>
    <parameter>10 to 16</parameter>
    <new_node>data</new_node>
  </item>
  <item id="2">
    <item_number>RJ12</item_number>
    <title>Another item with a 12</title>
    <setting>7 to 35</setting>
    <parameter>1 to 34</parameter>
    <new_node>goes</new_node>
  </item>
  <item id="3">
    <item_number>LN90</item_number>
    <title>LN with 90</title>
    <setting>3 to 35</setting>
    <parameter>9 to 50</parameter>
    <new_node>here</new_node>
    <unique_node>test</unique_node>
  </item>
</root>'

--build an XML object with the unique list of nodes
DECLARE @xmlcolumns XML;
WITH Xml_CTE AS
(
    SELECT
        CAST('<' + node.value('fn:local-name(.)',
            'varchar(100)') + '>' AS varchar(100) ) 
         + CAST('</' + node.value('fn:local-name(.)',
            'varchar(100)') + '>' AS varchar(100) ) AS name 
    FROM @xml.nodes('/root/item/*') AS roots(node)

)

SELECT @xmlcolumns = (
SELECT CONVERT(XML,name) FROM (
SELECT DISTINCT name 
FROM Xml_CTE
) a
FOR XML PATH(''), ROOT('root')
)



declare @SQL nvarchar(max) = ''
declare @Col nvarchar(max) = ', T.N.value(''[COLNAME][1]'', ''varchar(100)'') as [COLNAME]'  

--use the unique column list xml object to build the select statement
select @SQL = @SQL + replace(@Col, '[COLNAME]', T.N.value('local-name(.)', 'sysname'))
from @XMLcolumns.nodes('/root/*') as T(N) 

--build the entire query statement, using the original XML object as the data source
set @SQL = 'select '+stuff(@SQL, 1, 2, '')+' from @XML.nodes(''/root/item'') as T(N)' 

exec sp_executesql @SQL, N'@XML xml', @XML;

#1


11  

With a dynamic number of columns you need dynamic SQL.

使用动态数量的列,您需要动态SQL。

declare @XML xml = 
'<root>
  <item id="1">
    <item_number>IT23</item_number>
    <title>Item number twenty-three</title>
    <setting>5 to 20</setting>
    <parameter>10 to 16</parameter>
    <new_node>data</new_node>
  </item>
  <item id="2">
    <item_number>RJ12</item_number>
    <title>Another item with a 12</title>
    <setting>7 to 35</setting>
    <parameter>1 to 34</parameter>
    <new_node>goes</new_node>
  </item>
  <item id="3">
    <item_number>LN90</item_number>
    <title>LN with 90</title>
    <setting>3 to 35</setting>
    <parameter>9 to 50</parameter>
    <new_node>here</new_node>
  </item>
</root>'

declare @SQL nvarchar(max) = ''
declare @Col nvarchar(max) = ', T.N.value(''[COLNAME][1]'', ''varchar(100)'') as [COLNAME]' 

select @SQL = @SQL + replace(@Col, '[COLNAME]', T.N.value('local-name(.)', 'sysname'))
from @XML.nodes('/root/item[1]/*') as T(N)

set @SQL = 'select '+stuff(@SQL, 1, 2, '')+' from @XML.nodes(''/root/item'') as T(N)' 

exec sp_executesql @SQL, N'@XML xml', @XML

#2


1  

improving on Mikael's answer:

改进Mikael的答案:

With a dynamic number of columns you need dynamic SQL. This code will dynamically build a select statement supporting an unknown number of nodes, including items with different node lists.

使用动态数量的列,您需要动态SQL。此代码将动态构建支持未知数量节点的select语句,包括具有不同节点列表的项目。

declare @XML xml = 
'<root>
  <item id="1">
    <item_number>IT23</item_number>
    <title>Item number twenty-three</title>
    <setting>5 to 20</setting>
    <parameter>10 to 16</parameter>
    <new_node>data</new_node>
  </item>
  <item id="2">
    <item_number>RJ12</item_number>
    <title>Another item with a 12</title>
    <setting>7 to 35</setting>
    <parameter>1 to 34</parameter>
    <new_node>goes</new_node>
  </item>
  <item id="3">
    <item_number>LN90</item_number>
    <title>LN with 90</title>
    <setting>3 to 35</setting>
    <parameter>9 to 50</parameter>
    <new_node>here</new_node>
    <unique_node>test</unique_node>
  </item>
</root>'

--build an XML object with the unique list of nodes
DECLARE @xmlcolumns XML;
WITH Xml_CTE AS
(
    SELECT
        CAST('<' + node.value('fn:local-name(.)',
            'varchar(100)') + '>' AS varchar(100) ) 
         + CAST('</' + node.value('fn:local-name(.)',
            'varchar(100)') + '>' AS varchar(100) ) AS name 
    FROM @xml.nodes('/root/item/*') AS roots(node)

)

SELECT @xmlcolumns = (
SELECT CONVERT(XML,name) FROM (
SELECT DISTINCT name 
FROM Xml_CTE
) a
FOR XML PATH(''), ROOT('root')
)



declare @SQL nvarchar(max) = ''
declare @Col nvarchar(max) = ', T.N.value(''[COLNAME][1]'', ''varchar(100)'') as [COLNAME]'  

--use the unique column list xml object to build the select statement
select @SQL = @SQL + replace(@Col, '[COLNAME]', T.N.value('local-name(.)', 'sysname'))
from @XMLcolumns.nodes('/root/*') as T(N) 

--build the entire query statement, using the original XML object as the data source
set @SQL = 'select '+stuff(@SQL, 1, 2, '')+' from @XML.nodes(''/root/item'') as T(N)' 

exec sp_executesql @SQL, N'@XML xml', @XML;