Up until now you have talked about how to query XML, either a document or fragment, and return tabular data. The FOR XML clause does the reverse, meaning it takes tabular data and returns XML.
The FOR XML clause is a rowset aggregation function that returns a one-row, one-column result set containing an NVARCHAR(MAX) value. Several directives can be applied to the FOR XML clause, which provides different control and structure over the resulting XML. This section discusses the FOR XML clause and the different directives that can be applied.
Note
Before discussing the modes, you need to understand that the XMLDATA directive to the FOR XML options has been deprecated in SQL Server 2012. In its place you should use XSD generation when using the RAW and AUTO modes.
Auto
The AUTO directive is the easiest of the directives to use in which to generate XML output from results specified in the SELECT
statement. Although it is certainly the easiest of the directives to
use, it doesn't provide a lot of control over the resulting structure
of the XML output.
The key to the AUTO
directive is in its name, in that it “automatically” names the elements
and hierarchies based on table and column names, any aliases used, and
joins in the query.
For example, the following code illustrates a simple FOR XML AUTO clause:
SELECT CustomerID, OrderNumber, OrderDate
FROM Orders
FOR XML AUTO
The results show that the element name is taken from the table in which the data comes from.
<Orders CustomerID="1" OrderNumber="10001" OrderDate="2011-06-15T00:00:00" />
<Orders CustomerID="2" OrderNumber="10002" OrderDate="2011-06-16T00:00:00" />
<Orders CustomerID="1" OrderNumber="10003" OrderDate="2011-06-17T00:00:00" />
<Orders CustomerID="2" OrderNumber="10004" OrderDate="2011-06-18T00:00:00" />
You can change the name of the element by aliasing the table name in the query:
SELECT CustomerID, OrderNumber, OrderDate
FROM Orders o
FOR XML AUTO
/*
<o CustomerID="1" OrderNumber="10001" OrderDate="2011-06-15T00:00:00" />
<o CustomerID="2" OrderNumber="10002" OrderDate="2011-06-16T00:00:00" />
<o CustomerID="1" OrderNumber="10003" OrderDate="2011-06-17T00:00:00" />
<o CustomerID="2" OrderNumber="10004" OrderDate="2011-06-18T00:00:00" />
*/
The two previous examples produced XML fragments,
not documents. A valid XML document can have only a single top-level
node. You can add that by using the ROOT directive and specifying the name of the element:
SELECT CustomerID, OrderNumber, OrderDate
FROM Orders
FOR XML AUTO, ROOT('TodaysOrders')
/*
<TodaysOrders>
<Orders CustomerID="1" OrderNumber="10001" OrderDate="2011-06-15T00:00:00" />
<Orders CustomerID="2" OrderNumber="10002" OrderDate="2011-06-16T00:00:00" />
<Orders CustomerID="1" OrderNumber="10003" OrderDate="2011-06-17T00:00:00" />
<Orders CustomerID="2" OrderNumber="10004" OrderDate="2011-06-18T00:00:00" />
</TodaysOrders>
*/
The XML AUTO clause is also smart enough to generate hierarchical XML based on queries that use joins:
SELECT o.OrderNumber, o.OrderDate, c.Name
FROM Orders o
INNER JOIN Customer c ON o.CustomerID = c.CustomerID
FOR XML AUTO
/*
<o OrderNumber="10001" OrderDate="2011-06-15T00:00:00">
<c Name="Scott" />
</o>
<o OrderNumber="10002" OrderDate="2011-06-16T00:00:00">
<c Name="Adam" />
</o>
<o OrderNumber="10003" OrderDate="2011-06-17T00:00:00">
<c Name="Scott" />
</o>
<o OrderNumber="10004" OrderDate="2011-06-18T00:00:00">
<c Name="Adam" />
</o>
*/
The AUTO
clause works by generating elements for each row in which values are
created as attributes. You can take the previous query and apply the ELEMENTS directive to it to turn the attributes into elements:
SELECT o.OrderNumber, o.OrderDate, c.Name
FROM Orders o
INNER JOIN Customer c ON o.CustomerID = c.CustomerID
FOR XML AUTO, ELEMENTS
/*
<o>
<OrderNumber>10001</OrderNumber>
<OrderDate>2011-06-15T00:00:00</OrderDate>
<c>
<Name>Scott</Name>
</c>
</o>
<o>
<OrderNumber>10002</OrderNumber>
<OrderDate>2011-06-16T00:00:00</OrderDate>
<c>
<Name>Adam</Name>
</c>
</o>
<o>
<OrderNumber>10003</OrderNumber>
<OrderDate>2011-06-17T00:00:00</OrderDate>
<c>
<Name>Scott</Name>
</c>
</o>
<o>
<OrderNumber>10004</OrderNumber>
<OrderDate>2011-06-18T00:00:00</OrderDate>
<c>
<Name>Adam</Name>
</c>
</o>
*/
Although the previous query works, the problem is no one knows what c and o are. XML is all about describing the data, so remove the alias on the customer and rename the Orders alias to [Order].
SELECT [Order].OrderNumber, [Order].OrderDate, Customer.Name
FROM Orders [Order]
INNER JOIN Customer ON [Order].CustomerID = Customer.CustomerID
FOR XML AUTO, ELEMENTS
Now the XML is more readable.
<Order>
<OrderNumber>10001</OrderNumber>
<OrderDate>2011-06-15T00:00:00</OrderDate>
<Customer>
<Name>Scott</Name>
</Customer>
</Order>
<Order>
<OrderNumber>10002</OrderNumber>
<OrderDate>2011-06-16T00:00:00</OrderDate>
<Customer>
<Name>Adam</Name>
</Customer>
</Order>
<Order>
<OrderNumber>10003</OrderNumber>
<OrderDate>2011-06-17T00:00:00</OrderDate>
<Customer>
<Name>Scott</Name>
</Customer>
</Order>
<Order>
<OrderNumber>10004</OrderNumber>
<OrderDate>2011-06-18T00:00:00</OrderDate>
<Customer>
<Name>Adam</Name>
</Customer>
</Order>