XML Querying with SQL Server – The Basics
SQL Server allows us to output the result set of a query as XML, it also allows us to shred the contents of an XML file into a table.
An XML document is comprised of Tags and these start with < and end with /> so the following is an example of a tag. XML documents are ordered meaning that the position of the elements within the well-formed document matter.
<Customers>
More XML Here
</customers>
It’s worth remembering that XML documents are case-sensitive. XML documents have attributes an attribute is the following as shown in the below example.
<Customers>
<Customer custid=”1” CustomerName=”Max Smith”/>
</Customers>
In the example shown above an attribute is custid=”1” . When your XML document contains attributes it’s know as attribute-centric presentation. If your XML document does not contain attributes it’s know as element-centric presentation.
Producing XML using Queries
The following examples used the WideWorldImporters Database running on SQL server 2017. When creating XML documents, we use the FOR XML clause the first option is RAW.
SELECT SH.[StockItemID]
,SI.StockItemName
,[QuantityOnHand]
,[BinLocation]
,[LastStocktakeQuantity]
,[LastCostPrice]
,[ReorderLevel]
,[TargetStockLevel]
,[LastEditedWhen]
FROM [WideWorldImporters].[Warehouse].[StockItemHoldings] SH
inner join Warehouse.StockItems as SI
ON SI.StockItemID = SH.StockItemID
for xml raw
This produced the following output
<row StockItemID="1" StockItemName="USB missile launcher (Green)" QuantityOnHand="175609" BinLocation="L-1" LastStocktakeQuantity="171341" LastCostPrice="9.50" ReorderLevel="20" TargetStockLevel="100" LastEditedWhen="2016-05-31T07:00:00" />
<row StockItemID="2" StockItemName="USB rocket launcher (Gray)" QuantityOnHand="165538" BinLocation="L-1" LastStocktakeQuantity="161435" LastCostPrice="9.50" ReorderLevel="20" TargetStockLevel="100" LastEditedWhen="2016-05-31T12:00:00" />
<row StockItemID="3" StockItemName="Office cube periscope (Black)" QuantityOnHand="253190" BinLocation="L-2" LastStocktakeQuantity="246900" LastCostPrice="11.25" ReorderLevel="10" TargetStockLevel="120" LastEditedWhen="2016-05-31T12:00:00" />
<row StockItemID="4" StockItemName="USB food flash drive - sushi roll" QuantityOnHand="208109" BinLocation="L-3" LastStocktakeQuantity="202964" LastCostPrice="12.00" ReorderLevel="5" TargetStockLevel="100" LastEditedWhen="2016-05-31T12:00:00" />
The above output shows that this is an XML fragment because it does not contain a root node. We can change this by including the ROOT(‘NAME’) option as shown.
SELECT SH.[StockItemID]
,SI.StockItemName
,[QuantityOnHand]
,[BinLocation]
,[LastStocktakeQuantity]
,[LastCostPrice]
,[ReorderLevel]
,[TargetStockLevel]
,[LastEditedWhen]
FROM [WideWorldImporters].[Warehouse].[StockItemHoldings] SH
inner join Warehouse.StockItems as SI
ON SI.StockItemID = SH.StockItemID
for xml raw, root('Stock')
This produced the following:
<Stock>
<row StockItemID="1" StockItemName="USB missile launcher (Green)" QuantityOnHand="175609" BinLocation="L-1" LastStocktakeQuantity="171341" LastCostPrice="9.50" ReorderLevel="20" TargetStockLevel="100" LastEditedWhen="2016-05-31T07:00:00" />
<row StockItemID="2" StockItemName="USB rocket launcher (Gray)" QuantityOnHand="165538" BinLocation="L-1" LastStocktakeQuantity="161435" LastCostPrice="9.50" ReorderLevel="20" TargetStockLevel="100" LastEditedWhen="2016-05-31T12:00:00" />
<row StockItemID="3" StockItemName="Office cube periscope (Black)" QuantityOnHand="253190" BinLocation="L-2" LastStocktakeQuantity="246900" LastCostPrice="11.25" ReorderLevel="10" TargetStockLevel="120" LastEditedWhen="2016-05-31T12:00:00" />
<row StockItemID="4" StockItemName="USB food flash drive - sushi roll" QuantityOnHand="208109" BinLocation="L-3" LastStocktakeQuantity="202964" LastCostPrice="12.00" ReorderLevel="5" TargetStockLevel="100" LastEditedWhen="2016-05-31T12:00:00" />
</stock>
To have the XML document returned as element-centric we add ELEMENTS to the options as shown below.
SELECT SH.[StockItemID]
,SI.StockItemName
,[QuantityOnHand]
,[BinLocation]
,[LastStocktakeQuantity]
,[LastCostPrice]
,[ReorderLevel]
,[TargetStockLevel]
,[LastEditedWhen]
FROM [WideWorldImporters].[Warehouse].[StockItemHoldings] SH
inner join Warehouse.StockItems as SI
ON SI.StockItemID = SH.StockItemID
for xml raw, elements,root('Stock')
This produces the following output
<Stock>
<row>
<StockItemID>1</StockItemID>
<StockItemName>USB missile launcher (Green)</StockItemName>
<QuantityOnHand>175609</QuantityOnHand>
<BinLocation>L-1</BinLocation>
<LastStocktakeQuantity>171341</LastStocktakeQuantity>
<LastCostPrice>9.50</LastCostPrice>
<ReorderLevel>20</ReorderLevel>
<TargetStockLevel>100</TargetStockLevel>
<LastEditedWhen>2016-05-31T07:00:00</LastEditedWhen>
</row>
</stock>
XML Namespaces
We can add namespaces to our queries to help to distinguish between elements from different business areas, different companies and departments. The below example shows how to do this, also you can alias namespaces just as you do with tables.
with xmlnamespaces('CustomerOrders' as co)
select [co:customers].custid, [co:customers].contactname, [co:customers].contacttitle, [co:orders].shipcountry, [co:orders].orderdate
from sales.customers as [co:customers]
inner join sales.Orders as [co:orders]
on [co:customers].custid= [co:orders].custid
order by [co:customers].custid
for xml auto, elements, root('Customers')
this produces the following output. (the below is just a snippet)
<Customers xmlns:co="CustomerOrders">
<co:customers>
<custid>1</custid>
<contactname>Allen, Michael</contactname>
<contacttitle>Sales Representative</contacttitle>
<co:orders>
<shipcountry>Germany</shipcountry>
<orderdate>2015-08-25</orderdate>
</co:orders>
<co:orders>
<shipcountry>Germany</shipcountry>
<orderdate>2015-10-03</orderdate>
</co:orders>
<co:orders>
<shipcountry>Germany</shipcountry>
<orderdate>2015-10-13</orderdate>
</co:orders>
<co:orders>
<shipcountry>Germany</shipcountry>
<orderdate>2016-01-15</orderdate>
</co:orders>
<co:orders>
<shipcountry>Germany</shipcountry>
<orderdate>2016-03-16</orderdate>
</co:orders>
<co:orders>
It’s important to use the ORDER BY when producing XML from a SELECT statement. The reason being is that since we are formatting the XML by using the SELECT statement the ORDER BY gives us predictable results where as without the ORDER BY we would get unpredictable results.
You also need to make sure that the order of columns is correct in your SELECT list. This is due to the fact that the SELECT is formatting the XML document.