SQL для преобразования XML в таблицу - PullRequest
2 голосов
/ 19 мая 2011

В SQL 2005 есть ли способ преобразовать следующий xml в таблицу?

<root>
  <r>
    <data>"col1"</data>
    <data>"col2"</data>
    <data>"col3"</data>
  </r>
  <r>
    <data>"data1"</data>
    <data>""</data>
    <data>"data3"</data>
  </r>
  <r>
    <data>"data"</data>
    <data>"data"</data>
    <data>"data"</data>
  </r>
</root>

Я хочу, чтобы выходные данные были

col1 col2 col3
----------------
data      data3
data data data

XML может иметь другое числостолбцов, поэтому решение должно учитывать это.

Заранее спасибо.

Ответы [ 3 ]

3 голосов
/ 19 мая 2011
declare @xml xml
set @xml = 
'<root>
  <r>
    <data>"col1"</data>
    <data>"col2"</data>
    <data>"col3"</data>
  </r>
  <r>
    <data>"data1"</data>
    <data>""</data>
    <data>"data3"</data>
  </r>
  <r>
    <data>"data"</data>
    <data>"data"</data>
    <data>"data"</data>
  </r>
</root>'

declare @SQL nvarchar(max)
set @SQL = ''

select @SQL = @SQL + ',replace(r.r.value(''data['+
         cast(T.rn as nvarchar(10))+
         ']'', ''varchar(10)''), ''"'','''') as '+
         quotename(replace(T.ColName, '"', '')) 
from
(
  select
    r.r.value('.', 'sysname') as ColName,
    row_number() over(order by (select 1)) as rn
  from @xml.nodes('/root/r[1]/data') r(r)
) as T

set @SQL = 'select '+stuff(@SQL, 1, 1, '')+
        ' from @x.nodes(''/root/r[position()>1]'') r(r)'

exec sp_executesql @SQL, N'@x xml', @x = @xml

Поскольку здесь я использую динамический SQL, уместно предложить прочитать Проклятие и благословения динамического SQL .

Объяснение того, что происходит.

Этот запрос используется для получения имен столбцов из первого r узла:

select
    r.r.value('.', 'varchar(10)') as ColName,
    row_number() over(order by (select 1)) as rn
  from @xml.nodes('/root/r[1]/data') r(r)

/root/r[1] гарантирует, что мы получим первую строку.row_number() перечисляет столбцы, устанавливающие связь между числом и именем столбца.

В результате запрос в @SQL выглядит так:

select 
  replace(r.r.value('data[1]', 'varchar(10)'), '"','') as [col1],
  replace(r.r.value('data[2]', 'varchar(10)'), '"','') as [col2],
  replace(r.r.value('data[3]', 'varchar(10)'), '"','') as [col3] 
from @xml.nodes('/root/r[position()>1]') r(r)

/root/r[position()>1] получает все r узлыкроме первого.1 в data[1] происходит от row_number(), а [col1] происходит от имени соответствующего столбца.quotename() добавляет скобки [] к псевдониму столбца.Без quotename() этот запрос можно использовать для внедрения SQL.replace() используется для удаления " из строки.Он удалит все вхождения ", поэтому, если вы ожидаете, что " будет частью значения, вы можете использовать substring() для удаления ".

Я использовал varchar(10) в качестве размераданных столбца.Вы должны изменить это к тому, что вам нужно.

2 голосов
/ 19 мая 2011

Не с переменным количеством столбцов: SQL в целом является фиксированным столбцом

Тем не менее, вы можете ожидать это несколько

DECLARE @foo AS xml = '<root>
  <r>
    <data>"col1"</data>
    <data>"col2"</data>
    <data>"col3"</data>
  </r>
  <r>
    <data>"data1"</data>
    <data>""</data>
    <data>"data3"</data>
  </r>
  <r>
    <data>"data"</data>
    <data>"data"</data>
    <data>"data"</data>
  </r>
</root>'

SELECT
   REPLACE(x.item.value('(data)[1]', 'varchar(100)'), '"', '') AS col1,
   REPLACE(x.item.value('(data)[2]', 'varchar(100)'), '"', '') AS col2,
   REPLACE(x.item.value('(data)[3]', 'varchar(100)'), '"', '') AS col3,
   REPLACE(x.item.value('(data)[4]', 'varchar(100)'), '"', '') AS col4,
   REPLACE(x.item.value('(data)[5]', 'varchar(100)'), '"', '') AS col5,
   REPLACE(x.item.value('(data)[6]', 'varchar(100)'), '"', '') AS col6,
   REPLACE(x.item.value('(data)[7]', 'varchar(100)'), '"', '') AS col7,
   REPLACE(x.item.value('(data)[8]', 'varchar(100)'), '"', '') AS col8,
   REPLACE(x.item.value('(data)[9]', 'varchar(100)'), '"', '') AS col9,
   REPLACE(x.item.value('(data)[10]', 'varchar(100)'), '"', '') AS col10
FROM
   @foo.nodes('/root/r') x(item)

По умолчанию также нет гарантии оценки порядка узлов, что усложняет извлечение имен столбцов первой строки.

На основании этого ответа: http://stackoverflow.com/q/1134075/27535, вы можете использовать этот SQL для идентификации строки 1

;WITH n(i) AS (SELECT 0 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9),
     o(i) AS (SELECT n3.i * 100 + n2.i * 10 + n1.i FROM n n1, n n2, n n3)
SELECT
   REPLACE(x.item.value('(data)[1]', 'varchar(100)'), '"', '') AS col1,
   REPLACE(x.item.value('(data)[2]', 'varchar(100)'), '"', '') AS col2,
   REPLACE(x.item.value('(data)[3]', 'varchar(100)'), '"', '') AS col3,
   REPLACE(x.item.value('(data)[4]', 'varchar(100)'), '"', '') AS col4,
   REPLACE(x.item.value('(data)[5]', 'varchar(100)'), '"', '') AS col5,
   REPLACE(x.item.value('(data)[6]', 'varchar(100)'), '"', '') AS col6,
   REPLACE(x.item.value('(data)[7]', 'varchar(100)'), '"', '') AS col7,
   REPLACE(x.item.value('(data)[8]', 'varchar(100)'), '"', '') AS col8,
   REPLACE(x.item.value('(data)[9]', 'varchar(100)'), '"', '') AS col9,
   REPLACE(x.item.value('(data)[10]', 'varchar(100)'), '"', '') AS col10,
   o.i
FROM
   o
   CROSS APPLY
   @foo.nodes('/root/r[sql:column("o.i")]') x(item)
0 голосов
/ 19 мая 2011

Я никогда не делал этого, но я знаю, что это можно сделать ...

Взгляните на этот урок, кажется, он довольно близок к тому, что вы просите.http://weblogs.sqlteam.com/mladenp/archive/2007/06/18/60235.aspx

...