2013-12-11 1 views
1

Простейший способ анализа XML в столбцах с SQL Server. Пример:Анализ XML в столбцах с использованием SQL Server

<ns0:root xmlns:ns0="http://herp..."> 
    <ns1:FirstElement xmlns:ns1="http://derpyderp..."> 
     <ns1:FirstElementID>AAF11303</ns1:FirstElementID> 
     <ns1:FirstEValue>some random text</ns1:FirstEValue> 
     <ns1:SecondElement> 
      <ns1:Something>Asdsad</ns1:Something> 
      <ns1:Else> 
       <ns1:Stuff>sdf</ns1:Stuff> 
       <ns1:StuffVal>15</ns1:StuffVal> 
      </ns1:Else> 
      <ns1:Else> 
       <ns1:Stuff>jarjar</ns1:Stuff> 
       <ns1:StuffVal>16</ns1:StuffVal> 
       <ns1:StuffParam>true</ns1:StuffParam> 
      </ns1:Else> 
     </ns1:SecondElement> 
     <ns1:randValue>dosd</ns1:randValue> 
    </ns1:FirstElement> 
    <ns1:FirstElement> 
     <ns1:FirstElementID>DDF00301</ns1:FirstElementID> 
     <ns1:FirstEValue/> 
     <ns1:SecondElement> 
      <ns1:Else> 
       <ns1:Stuff>yessir</ns1:Stuff> 
       <ns1:StuffVal>0</ns1:StuffVal> 
      </ns1:Else> 
     </ns1:SecondElement>   
    </ns1:FirstElement> 
    <!-- ... times n the first element with a variating amount of children up to 15 levels deep --> 
</ns0:root> 

Я хотел бы это как простой выход колонки, а именно:

FIRSTELEMENTID | FIRSTEVALUE  | SOMETHING | ELSE.STUFF | ELSE.STUFFVAL | ELSE.STUFFPARAM | RANDVALUE 
'AAF11303'  |'some random text'| 'Asdasd' | 'sdf'  | 15   | NULL   | 'dosd' 
'AAF11303'  |'some random text'| 'Asdasd' | 'jarjar' | 16   | TRUE   | 'dosd' 
'DDF00301'  | NULL    | NULL  | 'yessir' | 0    | NULL   | NULL 

Теперь, фактическое XML является гораздо более сложным и все примеры, которые я видел до сих пор связаны с вручную анализируя XML и в основном передавая каждый дочерний элемент в виде собственного XML и анализируя его отдельно в следующем цикле скрипта и т. д. и т. д. и т. д.

Есть ли способ просто вывести значения так, как это? Вручную разбор нескольких МБ XML с бесчисленными вариациями, в которых появляются элементы, и сколько раз и где будет делать это в течение нескольких дней.

Я думал о создании сборки CLR, чтобы сделать это с помощью C# и передать результат в SQL как таблицу, но я хотел бы знать, есть ли еще один способ.

Спасибо!

ответ

2

В принципе, вы можете использовать методы nodes() и values() для анализа вашего xml (и with xmlnamespaces для обработки пространств имен). Что-то вроде этого:

;with xmlnamespaces('http://herp...' as ns0, 'http://derpyderp...' as ns1) 
select 
    T.C.value('(../../ns1:FirstElementID/text())[1]', 'nvarchar(max)') as FirstElementID, 
    T.C.value('(../../ns1:FirstEValue/text())[1]', 'nvarchar(max)') as FirstEValue, 
    T.C.value('(../ns1:Something/text())[1]', 'nvarchar(max)') as Something, 
    T.C.value('(ns1:Stuff/text())[1]', 'nvarchar(max)') as [Else.Stuff], 
    T.C.value('(ns1:StuffVal/text())[1]', 'nvarchar(max)') as [Else.StuffVal], 
    T.C.value('(ns1:StuffParam/text())[1]', 'nvarchar(max)') as [Else.StuffParam], 
    T.C.value('(../../ns1:randValue/text())[1]', 'nvarchar(max)') as [randValue] 
from @data.nodes('ns0:root/ns1:FirstElement/ns1:SecondElement/ns1:Else') as T(C) 

Это один создать одну строку для каждого ns0:root/ns1:FirstElement/ns1:SecondElement/ns1:Else, а затем принять все необходимые значения (некоторые из них взяты из родительских узлов). Обратите внимание, что если ваш First FirstElement не содержит ни одного из узлов ns1:SecondElement/ns1:Else, он не будет отображаться в наборе результатов. В этом случае, вы можете использовать запрос, как это:

;with xmlnamespaces('http://herp...' as ns0, 'http://derpyderp...' as ns1) 
select 
    F.C.value('(ns1:FirstElementID/text())[1]', 'nvarchar(max)') as FirstElementID, 
    F.C.value('(ns1:FirstEValue/text())[1]', 'nvarchar(max)') as FirstEValue, 
    S.C.value('(ns1:Something/text())[1]', 'nvarchar(max)') as Something, 
    E.C.value('(ns1:Stuff/text())[1]', 'nvarchar(max)') as [Else.Stuff], 
    E.C.value('(ns1:StuffVal/text())[1]', 'nvarchar(max)') as [Else.StuffVal], 
    E.C.value('(ns1:StuffParam/text())[1]', 'nvarchar(max)') as [Else.StuffParam], 
    F.C.value('(ns1:randValue/text())[1]', 'nvarchar(max)') as [randValue] 
from @data.nodes('ns0:root/ns1:FirstElement') as F(C) 
    outer apply F.C.nodes('ns1:SecondElement') as S(C) 
    outer apply S.C.nodes('ns1:Else') as E(C) 

sql fiddle demo

+0

Спасибо, кажется, именно то, что я ищу. Я проверю это и вернусь к вам. :) – Kahn

+1

Через год я забыл об этом, но этот ответ был абсолютно тем, что привело к правильному ответу. Я должен был вставлять XML из файла в таблицу, а затем индексировать XML, но в итоге мы обнаружили и исправили довольно много ошибок в старом решении, поняли, что весь бизнес нужен намного лучше, и превзошел прежнее решение с 15 раз лучше представление. Так что спасибо! – Kahn

Смежные вопросы