Select XML nodes as rows Select XML nodes as rows xml xml

Select XML nodes as rows


Here is your solution:

/* TEST TABLE */DECLARE @PEOPLE AS TABLE ([Name] VARCHAR(20),  [Address] XML )INSERT INTO @PEOPLE SELECT     'Joel',     '<address>      <street>Street 1</street>      <city>City 1</city>      <state>State 1</state>      <zipcode>Zip Code 1</zipcode>    </address>    <address>      <street>Street 2</street>      <city>City 2</city>      <state>State 2</state>      <zipcode>Zip Code 2</zipcode>    </address>'UNION ALL SELECT    'Kim',     '<address>      <street>Street 3</street>      <city>City 3</city>      <state>State 3</state>      <zipcode>Zip Code 3</zipcode>    </address>'SELECT * FROM @PEOPLE-- BUILD XMLDECLARE @x XMLSELECT @x = ( SELECT       [Name]    , [Address].query('            for $a in //address            return <address                 street="{$a/street}"                 city="{$a/city}"                 state="{$a/state}"                 zipcode="{$a/zipcode}"             />        ')   FROM @PEOPLE AS people   FOR XML AUTO) -- RESULTSSELECT [Name]    = T.Item.value('../@Name', 'varchar(20)'),       street    = T.Item.value('@street' , 'varchar(20)'),       city      = T.Item.value('@city'   , 'varchar(20)'),       state     = T.Item.value('@state'  , 'varchar(20)'),       zipcode   = T.Item.value('@zipcode', 'varchar(20)')FROM   @x.nodes('//people/address') AS T(Item)/* OUTPUT*/Name | street   | city   | state   | zipcode~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~Joel | Street 1 | City 1 | State 1 | Zip Code 1Joel | Street 2 | City 2 | State 2 | Zip Code 2Kim  | Street 3 | City 3 | State 3 | Zip Code 3


Here's how I do it generically:

I shred the source XML via a call such as

DECLARE @xmlEntityList xmlSET @xmlEntityList ='<ArbitrarilyNamedXmlListElement>              <ArbitrarilyNamedXmlItemElement><SomeVeryImportantInteger>1</SomeVeryImportantInteger></ArbitrarilyNamedXmlItemElement>              <ArbitrarilyNamedXmlItemElement><SomeVeryImportantInteger>2</SomeVeryImportantInteger></ArbitrarilyNamedXmlItemElement>              <ArbitrarilyNamedXmlItemElement><SomeVeryImportantInteger>3</SomeVeryImportantInteger></ArbitrarilyNamedXmlItemElement></ArbitrarilyNamedXmlListElement>'    DECLARE @tblEntityList  TABLE(        SomeVeryImportantInteger    int    )    INSERT @tblEntityList(SomeVeryImportantInteger)    SELECT         XmlItem.query('//SomeVeryImportantInteger[1]').value('.','int') as SomeVeryImportantInteger    FROM        [dbo].[tvfShredGetOneColumnedTableOfXmlItems] (@xmlEntityList)

by utilizing the scalar-valued function

/* Example Inputs *//*DECLARE @xmlListFormat xmlSET     @xmlListFormat =            '            <ArbitrarilyNamedXmlListElement>              <ArbitrarilyNamedXmlItemElement>004421UB7</ArbitrarilyNamedXmlItemElement>              <ArbitrarilyNamedXmlItemElement>59020UH24</ArbitrarilyNamedXmlItemElement>              <ArbitrarilyNamedXmlItemElement>542514NA8</ArbitrarilyNamedXmlItemElement>            </ArbitrarilyNamedXmlListElement>            'declare @tblResults TABLE (    XmlItem xml)*/-- =============================================-- Author:      6eorge Jetson-- Create date: 01/02/3003-- Description: Shreds a list of XML items conforming to--              the expected generic @xmlListFormat-- =============================================CREATE FUNCTION [dbo].[tvfShredGetOneColumnedTableOfXmlItems]  (    -- Add the parameters for the function here    @xmlListFormat xml)RETURNS @tblResults TABLE (    -- Add the column definitions for the TABLE variable here    XmlItem xml)ASBEGIN    -- Fill the table variable with the rows for your result set    INSERT @tblResults    SELECT        tblShredded.colXmlItem.query('.')   as XmlItem    FROM         @xmlListFormat.nodes('/child::*/child::*') as tblShredded(colXmlItem)    RETURN END--SELECT * FROM @tblResults


In case this is useful to anyone else out there looking for a "generic" solution, I created a CLR procedure that can take an Xml fragment as above and "shred" it into a tabular resultset, without you providing any additional information about the names or types of the columns, or customizing your call in any way for the given Xml fragment:

http://architectshack.com/ClrXmlShredder.ashx

There are of course some restrictions (the xml must be "tabular" in nature like this sample, the first row needs to contain all the elements/columns that will be supported, etc) - but I do hope it's a few steps ahead of what's available built-in.