4

Given this XML:

<Documents>
    <Batch BatchID = "1" BatchName = "Fred Flintstone">
        <DocCollection>
            <Document DocumentID = "269" KeyData = "" />
            <Document DocumentID = "6"   KeyData = "" />
            <Document DocumentID = "299" KeyData = ""     ImageFile="Test.TIF" />
        </DocCollection>    
    </Batch>    
    <Batch BatchID = "2" BatchName = "Barney Rubble">
        <DocCollection>
            <Document DocumentID = "269" KeyData = "" />
            <Document DocumentID = "6"   KeyData = "" />
        </DocCollection>
    </Batch>
</Documents>

I need to insert it into a table in SQL Server in this format:

BatchID   BatchName           DocumentID
1         Fred Flintstone     269
1         Fred Flintstone     6
1         Fred Flintstone     299
2         Barney Rubble       269
2         Barney Rubble       6

This SQL:

   SELECT
        XTbl.XCol.value('./@BatchID','int') AS BatchID,
        XTbl.XCol.value('./@BatchName','varchar(100)') AS BatchName,
        XTbl.XCol.value('DocCollection[1]/DocumentID[1]','int') AS DocumentID
   FROM @Data.nodes('/Documents/Batch') AS XTbl(XCol)

gets me this result:

BatchID BatchName       DocumentID
1       Fred Flintstone NULL
2       Barney Rubble   NULL

What am I doing wrong?

Also, can someone recommend a good tutorial for XML in SQL Server?

Thanks

Carl

1 Answer 1

7

You were close.

Using a wildcard and a CROSS APPLY, you can generate multiple records.

Changed alias to lvl1 and lvl2 to better illustrate.

Declare @XML xml = '
<Documents>
    <Batch BatchID = "1" BatchName = "Fred Flintstone">
        <DocCollection>
            <Document DocumentID = "269" KeyData = "" />
            <Document DocumentID = "6"   KeyData = "" />
            <Document DocumentID = "299" KeyData = ""     ImageFile="Test.TIF" />
        </DocCollection>    
    </Batch>    
    <Batch BatchID = "2" BatchName = "Barney Rubble">
        <DocCollection>
            <Document DocumentID = "269" KeyData = "" />
            <Document DocumentID = "6"   KeyData = "" />
        </DocCollection>
    </Batch>
</Documents>
'

Select BatchID    = lvl1.n.value('@BatchID','int') 
      ,BatchName  = lvl1.n.value('@BatchName','varchar(50)') 
      ,DocumentID = lvl2.n.value('@DocumentID','int') 
 From  @XML.nodes('Documents/Batch') lvl1(n)
 Cross Apply lvl1.n.nodes('DocCollection/Document') lvl2(n)

Returns

BatchID BatchName       DocumentID
1       Fred Flintstone 269
1       Fred Flintstone 6
1       Fred Flintstone 299
2       Barney Rubble   269
2       Barney Rubble   6
Sign up to request clarification or add additional context in comments.

5 Comments

Good solution, +1 from my side, but why the * in the first .nodes()?
@Shnugo Now that you mention it, /Batch would be safer/more specific. Or were you thinking something else?
No, just to avoid errors, if there might be more nodes with other names...
@Shnugo Updated, but the Cross Apply would still target DocCollection/Document. Just for fun I added <MiscOther><Document DocumentID = "269" KeyData = "" /></MiscOther> ... within a Batch and as a peer
Well, this is sophisticated :-D Tried to enclose your test <Document> with <DocCollcetion>?

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.