0

I have a XML file which is something like this

    declare @X XML
     Set @X  ='<A> 
        <B> 
        <C>161</C> 
        <D>190</D> 
        <E>43 ,44 ,48 ,49</E> 
        </B>
    <B> 
    <C>162</C> 
    <D>190</D> 
    <E>100</E> 
    </B>
    </A>
'

I want to parse it into a table:

    C         D       E
    161      190     43
    161      190     48
    161      190     49
    162      190     100

I tried using

select  
    x.r.value('(A)[1]', 'int') as C,
    x.r.value('(D)[1]', 'int') as D,
    x.r.value('(E)[1]', 'varchar(max)') as E
from    
    @X.nodes('/A/B') as x(r)

DECLARE @xml as xml, @str as varchar(100), @delimiter as varchar(10)

SET @str = '43,48,49'
SET @delimiter = ','
SET @xml = cast(('<X>'+replace(@str,@delimiter ,'</X><X>')+'</X>') as xml)
        SELECT T.N.value('.', 'int') as value FROM @xml.nodes('X') as T(N)

for separating the comma-separated column E into table.

But I am not able to combine both these in one query i.e. parse it into the table

2 Answers 2

2

Next time you should really avoid "chameleon" questions. Especially when changes to the initial questions invalidate correct answers this is considered to be impolite...

What you've done instead: Close this question by accepting the most helpful answer and start a new question.

Nevertheless here's a working approach:

DECLARE @X XML =
N'<A>
  <B>
    <C>161</C>
    <D>190</D>
    <E>43 ,44 ,48 ,49</E>
  </B>
  <B>
    <C>162</C>
    <D>190</D>
    <E>100</E>
  </B>
</A>';

WITH ReturnYourListAsXml AS
(
    SELECT CAST(N'<x>' + REPLACE(B.value(N'(E/text())[1]',N'nvarchar(max)'),',','</x><x>') + '</x>' AS XML) AS E_As_Xml
          ,B.value(N'(C/text())[1]',N'int') AS C
          ,B.value(N'(D/text())[1]',N'int') AS D
          ,ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS B_Nr 
    FROM @x.nodes(N'/A/B') AS AllNodes(B)
)
SELECT cte.B_Nr
      ,cte.C
      ,cte.D
      ,YourList.Member.value(N'text()[1]',N'int') AS E
FROM ReturnYourListAsXml AS cte
CROSS APPLY E_As_Xml.nodes(N'/x') AS YourList(Member);
Sign up to request clarification or add additional context in comments.

2 Comments

Hi, No XML is not under my control it should be comma seperated list only. The above query will work only if there is one node of <A> in case of multiple xml nodes of <A> this will result only first instance
I like your solution Shnugo. It's very succinct. I've upvoted it! Katherine
1

I modified my original answer to work with your modified question where you have multiple subordinate [B] nodes. I also took the liberty of modifying the data so that the result would be obvious (all the "1"s together, etc.). I do like the other answer here from Shnugo with the XML split string version. I haven't seen that in a while but I think it's cleaner so I used it.

DECLARE @builder AS TABLE
  (
       [C]   [INT]
       , [D] [INT]
       , [E] [XML]
  );
DECLARE @X XML ='<A> 
    <B> 
        <C>161</C> 
        <D>191</D> 
        <E>41 ,51 ,61 ,71</E> 
    </B>
    <B> 
        <C>162</C> 
        <D>192</D> 
        <E>102</E> 
    </B>
    <B> 
        <C>163</C> 
        <D>193</D> 
        <E>43, 53, 63</E> 
    </B>
</A>';

WITH [row_builder]
     AS (SELECT t.c.query(N'.') AS [b_row]
         FROM   @X.nodes(N'/A/B') AS [t]([c]))
   , [shredder]
     AS (SELECT [b_row].value(N'(./B/C/text())[1]', N'[INT]')             AS [C]
                , [b_row].value(N'(./B/D/text())[1]', N'[INT]')           AS [D]
                , [b_row].value(N'(./B/E/text())[1]', N'[NVARCHAR](MAX)') AS [E]
         FROM   [row_builder])
   , [splitter]
     AS (SELECT [C]
                , [D]
                , cast(( '<E>' + replace([E], N',', '</E><E>') + '</E>' ) AS XML) AS [E]
         FROM   [shredder])
SELECT [C]
       , [D]
       , [column].query(N'.').value(N'(/E/text())[1]', N'[INT]') AS [E]
FROM   [splitter] AS [entry]
       CROSS APPLY [E].[nodes]('/*') AS [table] ( [column] )
ORDER  BY [C]
          , [D]
          , [E]; 

5 Comments

The above query will work only if there is one node of <A> in case of multiple xml nodes of <A> this will result only first instance
That is correct. That is what the original poster asked for based on the reference XML.
SELECT @X.value(N'(/A/B/C/text())[1]', N'[INT]') AS [C], @X.value(N'(/A/B/D/text())[1]', N'[INT]') AS [D] is selecting only first value
Same goes for Splitter CTE
See my modification.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.