2016-11-28 4 views
1

Я пытаюсь разобрать столбец XML и пытаюсь сделать две вещейSQL Server 2012: синтаксический анализ XML затем выбрать один из подузлов, а также CONCAT всех субов узлов

  • перебирать несколько вложенных узлы узла и выбрать один узел на основе значения флага 1/0.
  • объединить все дочерние узлы, чтобы создать одну совмещенную поле (разделителями)

Runnable блок кода - SQL Server 2012:

create table #temp (XMLData xml) 

insert into #temp (XMLData) 
values (' 
<Report_Data> 
    <Report_Entry> 
    <IsActive>1</IsActive> 
    <PID>111</PID> 
    <Languages> 
     <Language>German</Language> 
     <speak>Y</speak> 
     <read>Y</read> 
     <write>Y</write> 
    </Languages> 
    <Languages> 
     <Language>Spanish</Language> 
     <speak>Y</speak> 
     <read>N</read> 
     <write>N</write> 
    </Languages> 
    <phone> 
     <PhoneNumber>(101)111-1111</PhoneNumber> 
     <PhoneType>Work</PhoneType> 
     <IsPrimary>1</IsPrimary> 
    </phone> 
    <phone> 
     <PhoneNumber>(101)111-2222</PhoneNumber> 
     <PhoneType>Mobile</PhoneType> 
     <IsPrimary>0</IsPrimary> 
    </phone> 
    </Report_Entry> 
    <Report_Entry> 
    <IsActive>1</IsActive> 
    <PID>222</PID> 
    <phone> 
     <PhoneNumber>(101)222-1111</PhoneNumber> 
     <PhoneType>Mobile</PhoneType> 
     <IsPrimary>0</IsPrimary> 
    </phone> 
    </Report_Entry> 
    <Report_Entry> 
    <IsActive>1</IsActive> 
    <PID>333</PID> 
    <phone> 
     <PhoneNumber>(101)333-1111</PhoneNumber> 
     <PhoneType>Phone</PhoneType> 
     <IsPrimary>0</IsPrimary> 
    </phone> 
    <phone> 
     <PhoneNumber>(101)333-2222</PhoneNumber> 
     <PhoneType>Mobile</PhoneType> 
     <IsPrimary>1</IsPrimary> 
    </phone> 
    <location> 
     <location-state>NY</location-state> 
    </location> 
    <location> 
     <location-state>DC</location-state> 
    </location> 
    </Report_Entry> 
</Report_Data> 
') 

select 
     c.value('IsActive[1]','varchar(1)') as IsActive 
    , c.value('PID[1]','varchar(5)') as PID 
    , case when c.value('phone[1]/IsPrimary[1]','int') = 1 then c.value('phone[1]/PhoneNumber[1]','varchar(15)') end as PublicWorkPhone /** this condition needs to look at all sub nodes. this stops at the first one. **/ 
    , c.value('location[1]','varchar(2)') as location 
from 
    #temp 
    cross apply #temp.XMLData.nodes('/Report_Data/Report_Entry') as y(c) 

drop table #temp 
GO 

Я получаю это:

IsActive PID PublicWorkPhone location 
-------- ----- --------------- -------- 
1  111 (101)111-1111 NULL 
1  222 NULL   NULL 
1  333 NULL   NY 

Однако, Мне нужно это:

IsActive PID PublicWorkPhone location 
-------- ----- --------------- -------- 
1  111 (101)111-1111 NULL 
1  222 NULL   NULL 
1  333 (101)333-2222 NY,DC 

Для PID = 333 основным телефоном является (101)333-2222 и не равно нулю. Кроме того, место должно быть «NY, DC», а не только NY.

Я очень благодарен за любую помощь, которую вы могли бы предложить для достижения результата. Спасибо

+0

Это хороший вопрос: сценарий сценариев с копированием, собственный результат, неправильный вывод, ожидаемый результат. +1 с моей стороны – Shnugo

+0

@ Шнуго, спасибо! – sch

ответ

0

Это должно сделать трюк:

WITH prep AS 
(
    SELECT 
    c.value('(IsActive[1]/text())[1]','char(1)') as IsActive, 
    c.value('(PID[1]/text())[1]','varchar(5)') as PID, 
    c.value('(phone[IsPrimary=1]/PhoneNumber/text())[1]', 'varchar(15)') AS PublicWorkPhone, 
    location = cc.value('(text())[1]', 'varchar(1000)') 
    from #temp 
    CROSS APPLY #temp.XMLData.nodes('/Report_Data/Report_Entry') as y(c) 
    OUTER APPLY c.nodes('location/location-state') AS z(cc) 
) 
SELECT 
    IsActive, 
    PID, 
    PublicWorkPhone, 
    Location = 
    STUFF ((
    SELECT ',' + location 
    FROM prep pp 
    WHERE p.PID = pp.PID 
    FOR XML PATH('')),1,1,'') 
FROM prep p 
GROUP BY IsActive, PID, PublicWorkPhone; 
+0

Благодарим вас за решение. Это сделало трюк для меня. – sch

1

Мое предложение использует XQuery в .nodes() найти узел телефона, который является основным , и XQuery -функцию data() для вашего местоположения. Эта функция вернется со всеми включенными текстовыми частями, разделенными пробелом (Btw: странно, что нельзя передать в разделителе char ...). В вашем случае я ожидаю твердый-два-char кодов местоположения. Просто замените заготовку запятой и все, что угодно ...

SELECT re.value(N'IsActive[1]','bit') AS IsActive 
     ,re.value(N'PID[1]','int') AS PID 
     ,ph.value(N'PhoneNumber[1]','nvarchar(max)') AS PublicWorkPhone 
     ,REPLACE(re.query(N'data(location/location-state)').value('.','nvarchar(max)'),' ',',') AS location 
FROM #temp AS tmp 
CROSS APPLY tmp.XMLData.nodes(N'/Report_Data/Report_Entry') AS A(re) 
OUTER APPLY re.nodes(N'phone[IsPrimary=1]') AS B(ph); 
+0

Благодарим вас за помощь в моей просьбе. Я буду использовать вашу логику замены, чтобы получить NY, DC. Таким образом, я работаю только с одним столом. – sch

+0

@sch Я не совсем понимаю, что ваш * То, что я работаю только с одной таблицей *, на самом деле, но я рад, что вы нашли решение! Пожалуйста, дайте мне один намек: SO-способ сказать * Спасибо * - это голосование и принятие. Вы приняли другой ответ. Это совершенно нормально и полностью зависит от вас. Поскольку вы пересекли границу 15 пунктов, вам также разрешено голосовать. Голоса подсчитываются для значков и привилегий. – Shnugo

+0

Я проголосовал за ваш ответ. Я принял другое решение, так как решил эту проблему для меня, как есть. когда телефон был недоступен, он возвращал «NULL значения IsActive PID PublicWorkPhone Location» '-------- ----- --------------- --- ------ '' 1 111 (101)111-1111 NULL' '1 222 NULL NULL' ' 1 333 (101)333-2222 Нью-Йорк, DC' против 'IsActive ПИД PublicWorkPhone расположение ' ' -------- ----- --------------- --------- ' ' 1 111 (101) 111- 1111' '1 222 NULL' '1 333 (101)333-2222 NY, DC' – sch