views:

54

answers:

2

Hi,

I have a XML column which contains XML like this:

<Set>
    <Element>
        <ID>
            1
        </ID>
<List>
    <ListElement>
        <Part1>
            ListElement 1
        </Part1>
    </ListElement>
    <ListElement>
        <Part1>
            ListElement2
        </Part1>
    </ListElement>
</List>
    </Element>
    <Element>
        <ID>
            2
        </ID>
<List>
    <ListElement>
        <Part1>
            ListElement3
        </Part1>
    </ListElement>
    <ListElement>
        <Part1>
            ListElement4
        </Part1>
    </ListElement>
</List>
    </Element>
</Set>

I would like to shred this into a relation table containing this:

ID, ListElement
1, ListElement1
1, ListElement2
2, ListElement3
2, ListElement4

I am able to obtain the content of the Parts using something like this:

select      
    List.value('(Part1/text())[1]', 'varchar(max)') as test
from 
    Table CROSS APPLY 
      xml.nodes('// Element/List/ListElement') AS List(List)

but I have not yet achieved to keep the ‘foreign key’ (the ID value).

Thanks.

Best wishes,

Christian

A: 

Try this instead of your query:

SELECT      
    List.value('(../../ID)[1]', 'int') AS 'ID',
    List.value('(Part1/text())[1]', 'varchar(max)') as test
FROM
    dbo.Table 
CROSS APPLY 
    xml.nodes('/Set/Element/List/ListElement') AS List(List)

Updated again - now back to only one CROSS APPLY, reaching back up into the grand parent's "ID" element using ../../ID in XPath. Also eliminated the //Element XPath in favour of /Set/Element (the //Element xpaths are notoriously slow)

marc_s
The white spaces are there to make it look nice for this question - this is just for illustrative purposes.I also need a cross apply. @tmp would not work as I have several rows of the stuff above.
csetzkorn
I tried this and it does not work. It identifies all IDs in a row but return null for test.It can not work as it only returns the n IDs of each element. However each ELEMENT has y ListElements! So the number of rows returned should be much larger than n! Basically there is a m-to-m realtionhsip between Element and ListElement - this is the challenge and I hope this makes sense.
csetzkorn
Hi. Thanks this looks much better. I am currently trying to use:Parent.Node.nodes('/List/ListElement') AS List(List)instead of Parent.Node.nodes('List/ListElement') AS List(List)because the latter does not work. This seems to run but I am still waiting - it seems to take ages ...
csetzkorn
Well, shredding XML has never been known to be blindingly fast.....
marc_s
This runs now for 45min. the table only contains one row with 20000 Element nodes (it will contain 600ish rows eventually) . This should not take so long ...
csetzkorn
@csetzkorn: Updated my answer again - using only a single optimized CROSS APPLY now
marc_s
Hi,Thanks. This works fine. Going to the parent was the clue. Thanks again!
csetzkorn
A: 

In case anyone wants to do this with XQuery:


<Table>
   {for $listElement in $table//ListElement
    return
      <Row>
        {$listElement/../../ID}
        {$listElement/Part1}
      </Row>    
   }
</Table>

where $table is the original XML

returns

<Table>
    <Row>
        <ID> 1 </ID>
        <Part1> ListElement 1 </Part1>
    </Row>
    <Row>
        <ID> 1 </ID>
        <Part1> ListElement2 </Part1>
    </Row>
    <Row>
        <ID> 2 </ID>
        <Part1> ListElement3 </Part1>
    </Row>
    <Row>
        <ID> 2 </ID>
        <Part1> ListElement4 </Part1>
    </Row>
</Table>
Chris Wallace