0

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

0

2 Answers 2

1

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
Sign up to request clarification or add additional context in comments.

2 Comments

Trying to run it like this: select @xml.query(' /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;')') Doesn't seem to work
@koumides: fn:count() must have one argument. The first fn:max invocation counts the maximum number of element into some "column". So it must be max(*/count(*)).
0

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.

Comments

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.