【问题标题】:How can I make a SQL table from an XML file that will have a dynamic number of nodes?如何从具有动态节点数的 XML 文件创建 SQL 表?
【发布时间】:2012-02-08 21:17:06
【问题描述】:

我使用的是 SQL Server 2008。
任务:获取一个 XML 文件并将其解析为一个 (n) SQL 表。
问题:列数及其名称会因 XML 而异。

这里有一些代码:

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>'

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

我是这样得到上表的:

   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))

现在假设 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>

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

   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))

获取此表:

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

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

【问题讨论】:

  • 只是好奇,为什么需要从 XML 创建 SQL 表,而不是简单地存储 XML?您将如何处理具有一组未知列的 SQL 表,而您将无法处理存储在 XML 数据类型中的 XML?
  • 这就像在问,如果我不知道自己需要多少间卧室,我该如何盖房子?
  • Michael Fredrickson:我存储 XML,这只是一个简化的示例,但最终表格将返回到将生成的代码隐藏页面HTML 表格
  • @AaronBertrand,这更像是在问“如果我告诉你 X 是什么,你能为我建造一座有 X 间卧室的房子吗?”如果被问到这个问题,我认为承包商不会摸着头走开。是否有更好的方法来完成我在 SQL 中所做的事情是另一回事 :)
  • 为什么我们要谈论我在 21 个月前发表的评论,关于一个您在 21 个月前也接受了答案的问题?

标签: sql-server xml openxml


【解决方案1】:

对于动态列数,您需要动态 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

【讨论】:

  • 我认为这是我见过的最好的 TSQL/XML 诡计之一,它解决了我的问题 - 好棒
【解决方案2】:

改进 Mikael 的回答:

对于动态列数,您需要动态 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>
    <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;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-01-07
    • 1970-01-01
    • 1970-01-01
    • 2013-07-20
    • 2018-03-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多