koumides
koumides

Reputation: 2504

T-SQL on XML (using XQuery)

I have the below XML

<myroot>
<scene>
<sceneId>983247</sceneId>
<item>
<coordinates>
<coordinate>0</coordinate>
<coordinate>1</coordinate>
<coordinate>2</coordinate>
<coordinate>3</coordinate>
</coordinates>
<Values>
<Value>34</Value>
<Value>541</Value>
<Value>255</Value>
<Value>332</Value>
</Values>
</item>
</scene>
</myroot>

How can I get using TSQL the following result:

Col1 Col2
0    34    
1    541
2    255
3    332

Thanks,

M

Upvotes: 0

Views: 832

Answers (2)

Peter Radocchia
Peter Radocchia

Reputation: 11007

Here's my XML noob approach.

If you only trust the element sequencing, and not the coordinate values themselves being a sequence:

select
  coordinate  = max(case when element = 'coordinate' then elemval end)
, value       = max(case when element = 'Value' then elemval end)
from (
  select 
    element   = row.value('local-name(.)','varchar(32)')
  , elemval   = row.value('.','int')
  , position  = row.value('for $s in . return count(../*[. << $s]) + 1', 'int')
  from @xml.nodes('/myroot/scene/item/*/*') a (row)
  ) a
group by position

Alternatively written as two .nodes() and a JOIN (you get the idea).

If do you trust the coordinate numbering to be a sequence starting at zero:

select 
  coordinate = row.value('for $s in . return count(../*[. << $s]) + 1', 'int')
             - 1
, value      = row.value('.','int')
from @xml.nodes('/myroot/scene/item/Values/*') a (row)

If you only trust the coordinate numbering to be a sequence, but from an arbitrary seed:

select 
  coordinate = row.value('for $s in . return count(../*[. << $s]) + 1', 'int')
             + row.value('(/myroot/scene/item/coordinates/coordinate)[1]','int')
             - 1
, value      = row.value('.','int')
from @xml.nodes('/myroot/scene/item/Values/*') a (row)

Paths can be abbreviated:

  • /myroot/scene/item/*/* -> //item/*/*
  • /myroot/scene/item/Values/* -> //Values/*
  • /myroot/scene/item/coordinates/coordinate -> //coordinate

But I don't know the wisdom of this either way.

//item/*/* can probably be made more specific, so that it only includes coordinate and Value edge nodes, but I don't know the syntax.

Upvotes: 0

user357812
user357812

Reputation:

This XPath 2.0 expression:

/myroot/scene/item/
   string-join(for $pos in (0 to max(*/count(*)))
               return string-join(for $col in (1 to max(count(*)))
                                  return if ($pos=0)
                                         then concat('Col',$col)
                                         else *[$col]/*[$pos],
                                  ' '),
               '&#xA;')

Output:

Col1 Col2
0 34
1 541
2 255
3 332

Upvotes: 1

Related Questions