Как я могу сделать таблицу SQL из файла XML, который будет иметь динамическое число узлов? - PullRequest
7 голосов
/ 09 февраля 2012

Я использую 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, которая будет выглядеть следующим образом:

table snapshot

Вот как я получил приведенную выше таблицу:

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

Чтобы получить эту таблицу:

second table from XML

Таким образом, проблема в том, что дочерние узлы элемента будут различаться.

Как можно генерировать те же таблицы без указания столбцов?Есть ли какой-то другой подход, кроме использования OPENXML?

Ответы [ 2 ]

12 голосов
/ 09 февраля 2012

При динамическом количестве столбцов необходим динамический 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 голосов
/ 09 мая 2016

улучшается ответ Микаэля:

С динамическим числом столбцов вам нужен динамический 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;
...