How to return XML from SQL Server 2008 that is structured with multiple selections sharing a common parent

21,050

Solution 1

Try something like this - it uses FOR XML PATH and subselects to create the "linked" sub-nodes for a given customer (I limited this to two sub tables - but you should get the "gist" of it and be able to extend it to any number of linked subtables):

SELECT
    CustomerID AS '@CustomerID',
    CustName AS '@Name',

    (SELECT ProductName AS '@productname'
     FROM dbo.Products p
     WHERE p.CustomerID = c.CustomerID  
     FOR XML PATH('Product'), TYPE) AS 'Products',

    (SELECT HobbyName AS '@hobbyname'
     FROM dbo.Hobbies h 
     WHERE h.CUstomerID = c.CustomerID
     FOR XML PATH('Hobby'), TYPE) AS 'Hobbies'
FROM
    dbo.Customers c
FOR XML PATH('Customer'), ROOT('Customers')

Gives me an output something like:

<Customers>
  <Customer CustomerID="1" Name="Fred">
    <Products>
      <Product productname="Table" />
      <Product productname="Wardrobe" />
      <Product productname="Chair" />
    </Products>
    <Hobbies>
      <Hobby hobbyname="Golf" />
      <Hobby hobbyname="Swimming" />
    </Hobbies>
  </Customer>
  <Customer CustomerID="2" Name="Sue">
    <Products>
      <Product productname="CD Player" />
      <Product productname="Picture frame" />
    </Products>
    <Hobbies>
      <Hobby hobbyname="Dancing" />
      <Hobby hobbyname="Gardening" />
      <Hobby hobbyname="Reading" />
    </Hobbies>
  </Customer>
</Customers>

Solution 2

Using FOR XML RAW

IF OBJECT_ID ('tempdb..#customer') IS NOT NULL DROP TABLE #Customer
IF OBJECT_ID ('tempdb..#product') IS NOT NULL DROP TABLE #product
IF OBJECT_ID ('tempdb..#vehicle') IS NOT NULL DROP TABLE #Vehicle
IF OBJECT_ID ('tempdb..#hobbies') IS NOT NULL DROP TABLE #Hobbies

CREATE TABLE #Customer (id INT,name NVARCHAR(20))
INSERT INTO #customer SELECT 1,'Fred' UNION ALL SELECT 2,'Sue'

CREATE TABLE #product(customer_id INT, name NVARCHAR(20))
INSERT INTO #product
SELECt 1 AS id, 'table' as product 
UNION ALL SELECT 1 AS id, 'chair' as product 
UNION ALL SELECT 1 AS id, 'wardrobe' as product 
UNION ALL SELECT 2 AS id, 'CD Player' as product 
UNION ALL SELECT 2 AS id, 'Picture Frame' as product 


CREATE TABLE #vehicle(customer_id INT, name NVARCHAR(20),colour NVARCHAR(20))
INSERT INTO #vehicle
SELECt 1 AS id, 'Car' as vehicle,'red' as colour
UNION ALL SELECT 1 AS id, 'bicycle' as vehicle,'Blue' AS colour
UNION ALL SELECT 2 AS id, 'Car' as vehicle, 'Yellow' as colour


CREATE TABLE #hobbies(customer_id INT, name NVARCHAR(20))
INSERT INTO #hobbies
SELECt 1 AS id, 'Golf' as name
UNION ALL SELECT 1 AS id, 'Swimming' as name
UNION ALL SELECT 2 AS id, 'Dancing' as name
UNION ALL SELECT 2 AS id, 'Reading' as name

SELECT 
c.id AS id
,c.name AS name
,(SELECT p.name
    FROM #product p
    WHERE p.customer_id = c.id
    FOR XML RAW('Products'),TYPE) AS Products
,(SELECT h.name
    FROM #hobbies h
    WHERE h.customer_id = c.id
    FOR XML RAW('Hobbies'),TYPE) AS Hobbies
,(SELECT v.name,v.colour
    FROM #vehicle v
    WHERE v.customer_id = c.id
    FOR XML RAW('Vehicle'),TYPE) AS Vehicle
FROM #customer c
FOR XML RAW('Customer'), ROOT('Customers')

Solution 3

select
    c.customerid,
    c.name,
    (
        select p.productname
        from Products as p
        where p.customerid = c.customerid
        for xml raw('Product'), root('Products'), type
    ),
    (
        select h.hobbyname
        from Hobbies as h
        where h.customerid = c.customerid
        for xml raw('Hobby'), root('Hobbies'), type
    ),
    (
        select v.name, v.color
        from Vehicles as v
        where v.customerid = c.customerid
        for xml raw('Vehicle'), root('Vehicles'), type
    )
from Customers as c
for xml raw('Customer'), root('Customers')

=> sql fiddle demo

Share:
21,050
Matt
Author by

Matt

Updated on November 21, 2020

Comments

  • Matt
    Matt over 3 years

    I've tried using "FOR XML PATH", "FOR XML EXPLICIT" and "FOR XML AUTO" but the data is never structured with the correct heirarchy.

    Basically, I have one parent table (Customers) and 3 child tables. Each table has a customerid column. There is a one-to-many relationship from the Customers table to each of the 3 child tables.

    As a mock example, I have a parent "Customers" table, and I have 3 other tables - Products, Hobbies and Vehicles - all related to the Customers table by a customerid.

    What is the SQL code to achieve the following kind of structure -

    <Customers>
        <Customer customerid="1" name="Fred">
           <Products>
               <Product productname="table" />
               <Product productname="chair" />
               <Product productname="wardrobe" />
           </Products>
           <Hobbies>
               <Hobby hobbyname="Golf" />
               <Hobby hobbyname="Swimming" />
           </Hobbies>
           <Vehicles>
               <Vehicle name="Car" color="Red" />
               <Vehicle name="Bicycle" color="Blue" />
           </Vehicles>
        </Customer>
        <Customer customerid="2" name="Sue">
           <Products>
               <Product productname="CD player" />
               <Product productname="Picture frame" />
           </Products>
           <Hobbies>
               <Hobby hobbyname="Dancing" />
               <Hobby hobbyname="Reading" />
           </Hobbies>
           <Vehicles>
               <Vehicle name="Car" color="Yellow" />
           </Vehicles>
        </Customer>
    </Customers>