4

I am trying to create sub nodes of same name but having different data coming from different columns in a table using for xml. But i am not getting the expected output from the query i have built.

Can some one point me to the right way of building this query?

Sample table and the FOR XML query used given below:

;WITH Temp(id, name1, name2)
AS
(
    SELECT 1, 'A', 'B' UNION
    SELECT 2, 'C', 'D' UNION
    SELECT 3, 'E', 'F'
)
SELECT
    id
    ,name1 AS [names/name]
    ,name2 AS [names/name]
FROM
    Temp
FOR XML PATH('Data'), TYPE, ROOT('Feed')

Output :

<Feed>
  <Data>
    <id>1</id>
    <names>
      <name>AB</name>
    </names>
  </Data>
  <Data>
    <id>2</id>
    <names>
      <name>CD</name>
    </names>
  </Data>
  <Data>
    <id>3</id>
    <names>
      <name>EF</name>
    </names>
  </Data>
</Feed>

Expected output :

<Feed>
  <Data>
    <id>1</id>
    <names>
      <name>A</name>
      <name>B</name>
    </names>
  </Data>
  <Data>
    <id>2</id>
    <names>
      <name>C</name>
      <name>D</name>
    </names>
  </Data>
  <Data>
    <id>3</id>
      <name>E</name>
      <name>F</name>
    </names>
  </Data>
</Feed>
2
  • If you rename the alias for name2 column something other than [names/name] like [name2], you will get the expected output. Only thing the xml tag for that will not be <name></name> but <name2></name2>. You can find and replace it after generating the XML, if needed. Commented Jul 29, 2015 at 13:17
  • @AbhayChauhan that is what i am planning to do but only as a last resort. Commented Jul 29, 2015 at 13:27

4 Answers 4

1

You can select the names in a subquery

;WITH Temp(id, name1, name2)
AS
(
    SELECT 1, 'A', 'B' UNION
    SELECT 2, 'C', 'D' UNION
    SELECT 3, 'E', 'F'
)
SELECT
    id
    ,(SELECT name 
        FROM (
                SELECT name1 AS name 
                FROM Temp t2 
                WHERE t1.id = t2.id 
                UNION ALL 
                SELECT name2 AS name 
                FROM Temp t2 
                WHERE t1.id = t2.id) AS t 
        FOR XML PATH(''), TYPE) AS names
FROM
    Temp t1
FOR XML PATH('Data'), TYPE, ROOT('Feed')
Sign up to request clarification or add additional context in comments.

2 Comments

This works but can give performance issues when the source table has lots of data. Till then i am using the solution mentioned by @AbhayChauhan in his comment to the question.
if id is primary key it shouldn't be too slow.. but it's up to you
1

I guess, this should be quite efficient (works in SQL Server at least):

;WITH Temp(id, name1, name2)
AS
(
    SELECT 1, 'A', 'B' UNION
    SELECT 2, 'C', 'D' UNION
    SELECT 3, 'E', 'F'
)
SELECT
    id,
    (
        SELECT
            name1 AS name
            ,null
            ,name2 AS name
        FOR XML PATH(''), TYPE
    ) AS names
FROM
    Temp
FOR XML PATH('Data'), TYPE, ROOT('Feed')

Comments

0

You could do this quite nicely with a Cross Apply:

;WITH Temp(id, name1, name2)
AS
(
    SELECT 1, 'A', 'B' UNION
    SELECT 2, 'C', 'D' UNION
    SELECT 3, 'E', 'F'
)
SELECT
    id
    ,x.name AS [names/name]

FROM
    Temp
    CROSS APPLY 
        (VALUES
            (name1),
            (name2)
        ) x (name)
FOR XML PATH('Data'), TYPE, ROOT('Feed')

Comments

0

A little more elegant:

;WITH Temp(id, name1, name2)
AS
(
    SELECT 1 id, 'A', 'B' UNION
    SELECT 2 id, 'C', 'D' UNION
    SELECT 3 id, 'E', 'F'
)
SELECT
    id [Data/id],
    (select name1 name, null, name2 name 
     for xml path('names'), type) [Data]
FROM
    Temp
FOR XML PATH(''), TYPE, ROOT('Feed')

produces:

<Feed>
  <Data>
    <id>1</id>
    <names>
      <name>A</name>
      <name>B</name>
    </names>
  </Data>
  <Data>
    <id>2</id>
    <names>
      <name>C</name>
      <name>D</name>
    </names>
  </Data>
  <Data>
    <id>3</id>
    <names>
      <name>E</name>
      <name>F</name>
    </names>
  </Data>
</Feed>

Microsoft XML naturally collapses same name fields and removes empty values by default, so null becomes nothing and permits the same name fields to be side by side in the output.

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.