Wednesday, March 28, 2012

Property Promotion with multi-level XML data type

How would you extract data from an XML datatype column when it has multiple levels? I've done this with single levels using CROSS APPLY and the nodes method, but can't seem to grab data from the intermediate levels.

Example:

<Level1>

<Level2>xyz</Level2>

<Level2>xyz</Level2>

<Level3>abc</Level3>

<Level3>abc</Level3>

<Level3>abc</Level3>

<Level2>xyz</Level2>

<Level2>xyz</Level2>

</Level1>

For the root level, I use the xml.value() function, and for the details I use the cross apply xml.nodes() function typically, but if I use the xpath in the xml.nodes, I have to use the path to the lowest level (level3) to grab those values, but can't seem to grab the changing values at the intermediate levels (level2).

Would this involve multiple cross-apply instances?

I don't understand your example, why are those Level3 elements indented further to the right than the Level2 elements? They are both children of the root element Level1.

And it is not obvious what you want to extract.

|||

Sorry if this wasn't clear. The XML is supposed to represent a multiple-level heirarchy and the indentation indicates a parent-child relationship.

So level 1 can be Order, for example. At this level there can be several elements that define the Order (ID, Cust #, etc). Level 2 would be Order Type, like Internal, External, Global, Domestic (each order always has these 4 types associated with it). Level 3 would be details relevant to that order detail item, like part #, quantity, etc. (each Order has 4 Order Types, of which have multiple line-items within each order type).

So I need to return a table of data based on this single order. The results returned would have these fields:

Order ID (repeated for every order detail)

Cust # (repeated for every order detail)

OrderType (repeated for every order type/order detail)

Part # (unique per order detail/type)

Qty (unique per order detail/type)

Analogous to joining three relational tables (Order details -> Order Type -> Order) and getting the combined results of all three.

In SQL xquery, I use the .value function to get the Order ID and Cust# because they are at the root level of the XML field. I use the CROSS APPLY .nodes method to get to the lowest level of detail (Part# and Qty) and this produces the correct data. But for the intermediate level (Order Type) I can't seem to get to it.

If it still isn't clear, I'll send some actual XML.

Thanks

Kory

|||

Consider posting the XML and the query you have, then we can work from there to improve it.

|||

A coworker of mine helped me to figure this out:

I was doing this:

Before

With Namespaces(....)
select
t.EventDateTime
,t.OrderId

,ref.value('(//gns:OrderType/text())[1]','varchar(max)') OrderType
,ref.value('(ns:Product/text())[1]','varchar(max)') Product
,ref.value('(ns:Qty/text())[1]','varchar(max)') Qty
FROM
Orders t
CROSS APPLY
xmlfld.nodes ('//ns:OrderDetailItem') AS R(ref)

And we changed it to this:

After

With Namespaces(....)
select
t.EventDateTime
,t.OrderId

,ref.value('(http://gns:OrderType/text())[1]','varchar(max)') OrderType

,ref.value('(//gns:OrderType/text())[1]','varchar(max)') OrderType
,ref.value('(ns:Product/text())[1]','varchar(max)') Product
,ref.value('(ns:Qty/text())[1]','varchar(max)') Qty
FROM
Orders t
CROSS APPLY
xmlfld.nodes ('//ns:OrderDetailItem') AS R(ref)

So the only change was to the xpath from //gnsSurpriserderType to http://gnsSurpriserderType

I really don't understand why this worked, but the first way just promoted the first occurance of the value, but the second method promoted the value when it changed.

-Kory

No comments:

Post a Comment