SQL Server FOR XML PATH Examples

In SQL Server, the FOR XML clause allows us to return the results of a query as an XML document. Simply by placing the FOR XML clause at the end of the query will output the results in XML.

When we do this, we have the option of specifying RAW, AUTO, EXPLICIT, or PATH mode. These modes allow us to shape the resulting XML in different ways, and so the mode we choose will determine how the XML is generated.

Below are examples of using PATH mode when generating XML from a SQL query.

The Original Query

First, here’s the original query without the FOR XML clause:

SELECT
    p.PetId,
    p.PetName,
    p.DOB,
    pt.PetType,
    CONCAT(o.FirstName, ' ', o.LastName) AS OwnerName
FROM Pets p 
    INNER JOIN PetTypes pt ON p.PetTypeId = pt.PetTypeId
    INNER JOIN Owners o ON p.OwnerId = o.OwnerId;

Result:

PetId  PetName  DOB         PetType  OwnerName    
-----  -------  ----------  -------  -------------
1      Fluffy   2020-11-20  Cat      Nancy Simpson
2      Fetch    2019-08-16  Dog      Nancy Simpson
3      Scratch  2018-10-01  Cat      Bart Pitt    

This is what the data looks like when it’s in a table. The following examples turn that into XML documents.

Basic FOR XML PATH Example

Here’s what happens when we append FOR XML PATH to the previous query:

SELECT
    p.PetId,
    p.PetName,
    p.DOB,
    pt.PetType,
    CONCAT(o.FirstName, ' ', o.LastName) AS OwnerName
FROM Pets p 
    INNER JOIN PetTypes pt ON p.PetTypeId = pt.PetTypeId
    INNER JOIN Owners o ON p.OwnerId = o.OwnerId
FOR XML PATH;

When I run that with my command line tool, the XML is returned as one long string:

<row><PetId>1</PetId><PetName>Fluffy</PetName><DOB>2020-11-20</DOB><PetType>Cat</PetType><OwnerName>Nancy Simpson</OwnerName></row><row><PetId>2</PetId><PetName>Fetch</PetName><DOB>2019-08-16</DOB><PetType>Dog</PetType><OwnerName>Nancy Simpson</OwnerName></row><row><PetId>3</PetId><PetName>Scratch</PetName><DOB>2018-10-01</DOB><PetType>Cat</PetType><OwnerName>Bart Pitt</OwnerName></row>

The same happens when I run it in my GUI (Azure Data Studio). The XML is returned in one column and one row. However, when running it in my GUI, clicking on the XML in the column opens a new tab with the XML formatted like this:

<row>
	<PetId>
		1
	</PetId>
	<PetName>
		Fluffy
	</PetName>
	<DOB>
		2020-11-20
	</DOB>
	<PetType>
		Cat
	</PetType>
	<OwnerName>
		Nancy Simpson
	</OwnerName>
</row>
<row>
	<PetId>
		2
	</PetId>
	<PetName>
		Fetch
	</PetName>
	<DOB>
		2019-08-16
	</DOB>
	<PetType>
		Dog
	</PetType>
	<OwnerName>
		Nancy Simpson
	</OwnerName>
</row>
<row>
	<PetId>
		3
	</PetId>
	<PetName>
		Scratch
	</PetName>
	<DOB>
		2018-10-01
	</DOB>
	<PetType>
		Cat
	</PetType>
	<OwnerName>
		Bart Pitt
	</OwnerName>
</row>

By default, a <row> element is added for each row in the rowset. We can change that.

Rename the <row> Element

We can rename the <row> element by specifying the name in brackets at the end of the query:

SELECT
    p.PetId,
    p.PetName,
    p.DOB,
    pt.PetType,
    CONCAT(o.FirstName, ' ', o.LastName) AS OwnerName
FROM Pets p 
    INNER JOIN PetTypes pt ON p.PetTypeId = pt.PetTypeId
    INNER JOIN Owners o ON p.OwnerId = o.OwnerId
FOR XML PATH ( 'Pet' );

Result:

<Pet>
	<PetId>
		1
	</PetId>
	<PetName>
		Fluffy
	</PetName>
	<DOB>
		2020-11-20
	</DOB>
	<PetType>
		Cat
	</PetType>
	<OwnerName>
		Nancy Simpson
	</OwnerName>
</Pet>
<Pet>
	<PetId>
		2
	</PetId>
	<PetName>
		Fetch
	</PetName>
	<DOB>
		2019-08-16
	</DOB>
	<PetType>
		Dog
	</PetType>
	<OwnerName>
		Nancy Simpson
	</OwnerName>
</Pet>
<Pet>
	<PetId>
		3
	</PetId>
	<PetName>
		Scratch
	</PetName>
	<DOB>
		2018-10-01
	</DOB>
	<PetType>
		Cat
	</PetType>
	<OwnerName>
		Bart Pitt
	</OwnerName>
</Pet>

In this case I renamed the <row> element to <Pet>.

Remove the <row> Element

Alternatively, we can remove the <row> element altogether. To do this, just use an empty string inside the brackets:

SELECT
    p.PetId,
    p.PetName,
    p.DOB,
    pt.PetType,
    CONCAT(o.FirstName, ' ', o.LastName) AS OwnerName
FROM Pets p 
    INNER JOIN PetTypes pt ON p.PetTypeId = pt.PetTypeId
    INNER JOIN Owners o ON p.OwnerId = o.OwnerId
FOR XML PATH ( '' );

Result:

<PetId>
	1
</PetId>
<PetName>
	Fluffy
</PetName>
<DOB>
	2020-11-20
</DOB>
<PetType>
	Cat
</PetType>
<OwnerName>
	Nancy Simpson
</OwnerName>
<PetId>
	2
</PetId>
<PetName>
	Fetch
</PetName>
<DOB>
	2019-08-16
</DOB>
<PetType>
	Dog
</PetType>
<OwnerName>
	Nancy Simpson
</OwnerName>
<PetId>
	3
</PetId>
<PetName>
	Scratch
</PetName>
<DOB>
	2018-10-01
</DOB>
<PetType>
	Cat
</PetType>
<OwnerName>
	Bart Pitt
</OwnerName>

We can see that all elements are still returned, except for the wrapping element.

Create an Attribute

We can create an attribute by creating an alias that starts with @ and doesn’t contain /:

SELECT
    p.PetId AS "@id",
    p.PetName,
    p.DOB,
    pt.PetType,
    CONCAT(o.FirstName, ' ', o.LastName) AS OwnerName
FROM Pets p 
    INNER JOIN PetTypes pt ON p.PetTypeId = pt.PetTypeId
    INNER JOIN Owners o ON p.OwnerId = o.OwnerId
FOR XML PATH ( 'Pet' );

Result:

<Pet id="1">
	<PetName>
		Fluffy
	</PetName>
	<DOB>
		2020-11-20
	</DOB>
	<PetType>
		Cat
	</PetType>
	<OwnerName>
		Nancy Simpson
	</OwnerName>
</Pet>
<Pet id="2">
	<PetName>
		Fetch
	</PetName>
	<DOB>
		2019-08-16
	</DOB>
	<PetType>
		Dog
	</PetType>
	<OwnerName>
		Nancy Simpson
	</OwnerName>
</Pet>
<Pet id="3">
	<PetName>
		Scratch
	</PetName>
	<DOB>
		2018-10-01
	</DOB>
	<PetType>
		Cat
	</PetType>
	<OwnerName>
		Bart Pitt
	</OwnerName>
</Pet>

In this case I created an id attribute for the Pet element. The Pet element was defined at the end of the query, and the id attribute was defined when I created the alias for the PetId column.

Add a Root Element

We can add a root element by defining it at the end of the query:

SELECT
    p.PetId AS "@id",
    p.PetName,
    p.DOB,
    pt.PetType,
    CONCAT(o.FirstName, ' ', o.LastName) AS OwnerName
FROM Pets p 
    INNER JOIN PetTypes pt ON p.PetTypeId = pt.PetTypeId
    INNER JOIN Owners o ON p.OwnerId = o.OwnerId
FOR XML PATH ( 'Pet' ), ROOT ('Root');

Result:

<Root>
	<Pet id="1">
		<PetName>
			Fluffy
		</PetName>
		<DOB>
			2020-11-20
		</DOB>
		<PetType>
			Cat
		</PetType>
		<OwnerName>
			Nancy Simpson
		</OwnerName>
	</Pet>
	<Pet id="2">
		<PetName>
			Fetch
		</PetName>
		<DOB>
			2019-08-16
		</DOB>
		<PetType>
			Dog
		</PetType>
		<OwnerName>
			Nancy Simpson
		</OwnerName>
	</Pet>
	<Pet id="3">
		<PetName>
			Scratch
		</PetName>
		<DOB>
			2018-10-01
		</DOB>
		<PetType>
			Cat
		</PetType>
		<OwnerName>
			Bart Pitt
		</OwnerName>
	</Pet>
</Root>

We can provide a name for the root other than Root:

SELECT
    p.PetId AS "@id",
    p.PetName,
    p.DOB,
    pt.PetType,
    CONCAT(o.FirstName, ' ', o.LastName) AS OwnerName
FROM Pets p 
    INNER JOIN PetTypes pt ON p.PetTypeId = pt.PetTypeId
    INNER JOIN Owners o ON p.OwnerId = o.OwnerId
FOR XML PATH ( 'Pet' ), ROOT ('Pets');

Result:

<Pets>
	<Pet id="1">
		<PetName>
			Fluffy
		</PetName>
		<DOB>
			2020-11-20
		</DOB>
		<PetType>
			Cat
		</PetType>
		<OwnerName>
			Nancy Simpson
		</OwnerName>
	</Pet>
	<Pet id="2">
		<PetName>
			Fetch
		</PetName>
		<DOB>
			2019-08-16
		</DOB>
		<PetType>
			Dog
		</PetType>
		<OwnerName>
			Nancy Simpson
		</OwnerName>
	</Pet>
	<Pet id="3">
		<PetName>
			Scratch
		</PetName>
		<DOB>
			2018-10-01
		</DOB>
		<PetType>
			Cat
		</PetType>
		<OwnerName>
			Bart Pitt
		</OwnerName>
	</Pet>
</Pets>

Add a Hierarchy

We can create an alias with a “parent/child” pattern to create a hierarchy within the XML document:

SELECT
    p.PetId AS "@id",
    p.PetName AS "Details/Name",
    p.DOB AS "Details/DOB",
    pt.PetType AS "Details/Type",
    CONCAT(o.FirstName, ' ', o.LastName) AS OwnerName
FROM Pets p 
    INNER JOIN PetTypes pt ON p.PetTypeId = pt.PetTypeId
    INNER JOIN Owners o ON p.OwnerId = o.OwnerId
FOR XML PATH ( 'Pet' ), root ('Root');

Result:

<Root>
	<Pet id="1">
		<Details>
			<Name>
				Fluffy
			</Name>
			<DOB>
				2020-11-20
			</DOB>
			<Type>
				Cat
			</Type>
		</Details>
		<OwnerName>
			Nancy Simpson
		</OwnerName>
	</Pet>
	<Pet id="2">
		<Details>
			<Name>
				Fetch
			</Name>
			<DOB>
				2019-08-16
			</DOB>
			<Type>
				Dog
			</Type>
		</Details>
		<OwnerName>
			Nancy Simpson
		</OwnerName>
	</Pet>
	<Pet id="3">
		<Details>
			<Name>
				Scratch
			</Name>
			<DOB>
				2018-10-01
			</DOB>
			<Type>
				Cat
			</Type>
		</Details>
		<OwnerName>
			Bart Pitt
		</OwnerName>
	</Pet>
</Root>