3

I have the following table

name  | age | misc
------------------
david | 20  | foo
john  | 30  | bar

And I want to transform it into the following XML:

<doc>
  <field name="name" val="david" />
  <field name="age" val="20" />
  <field name="misc" val="foo" />
</doc>
<doc>
  <field name="name" val="john" />
  <field name="age" val="30" />
  <field name="misc" val="bar" />
</doc>

I have for this working below for a single column, however if I try to add a second column for another field node I get the error:

Msg 9303, Level 16, State 1, Line 25
XQuery [query()]: Syntax error near 'name', expected '}'.

This is a sample of what I am trying to do and is ready to run in SQL Server Management Studio. I can't find much documentation on the syntax and am quite lost for ideas.

Any assistance is appreciated!

declare @MyData table (name varchar(200), age varchar(200), misc varchar(200))

insert into @MyData values('david', '20', 'foo')
insert into @MyData values('john', '30', 'bar')

/*This one works fine*/
SELECT (select * from @MyData as MyData for xml auto, type).query
(
' for $d in /MyData
   return 
   <doc>{
     <field name="name" val="{data($d/@name)}"  />
  }</doc>'
)

/*This one is what I want*/
SELECT (select * from @MyData as MyData for xml auto, type).query
(
' for $d in /MyData
   return 
   <doc>{
     <field name="name" val="{data($d/@name)}"  />
     <field name="age" val="{data($d/@age)}"  />
     <field name="misc" val="{data($d/@misc)}"  />
  }</doc>'
)

2 Answers 2

4

How about this..

select 
    (select 'name' as 'field/@name', a.name as 'field/@val' for xml path(''), type),
    (select 'age' as 'field/@name', a.age as 'field/@val' for xml path(''), type),
    (select 'misc' as 'field/@name', a.misc as 'field/@val' for xml path(''), type)
from 
    MyData a for xml path('doc')

for your XQuery version try this: (I just removed the curly braces) Is that OK?

SELECT (select * from @MyData as MyData for xml auto, type).query
(
' for $d in /MyData
   return 
   <doc>
     <field name="name" val="{data($d/@name)}"  />
     <field name="age"  val="{data($d/@age)}"  />
     <field name="misc" val="{data($d/@misc)}"  />
  </doc>'
)
Sign up to request clarification or add additional context in comments.

1 Comment

I knew it was a simple syntax thing! I think I prefer the xquery version since I think it's a little more obvious what's happening. Thanks again
1

You're essentially trying to get XML for the unpivoted data. So, start by getting a unique row identifier that isn't a field (I'll use a CTE and row_number). From there, you can use UNPIVOT and FOR XML EXPLICIT:

;with data as (
    select name, age, misc,
        row_number() over(order by name) as 'row'
    from @MyData
)
select 1 as tag,
       null as parent,
       row as [doc!1!row!hide],
       null as [field!2!name],
       null as [field!2!val]
from data
UNION
select 2 as tag,
       1 as parent,
       row as [doc!1!row!hide],
       fieldName as [field!2!name],
       val as [field!2!val]
from data d
  UNPIVOT(val for fieldName in (name, age, misc)) up
order by row, tag
FOR XML EXPLICIT, ROOT('root')

the ROOT('root') is to add a simple root element and is in addition to the requested xml format, but I thought it might be useful.

UPDATE
After looking a bit closer at query execution plans, you might be better off simply creating formatted text then casting to xml:

select cast('<field name="name" val="'+name+'" />'+
            '<field name="age" val="'+age+'" />'+
            '<field name="misc" val="'+misc+'" />'
            as xml)
from @MyData
for xml path('doc')

1 Comment

Thanks for the answers. I was considering the second version if I would not get an xquery workaround, as it turns out I just needed to remove the braces! The unpivot looks interesting and may work well for another query I need!

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.