/*
This is a a script to introduce the FOR XML PATH clause in Sql Server 2005 in a step by step manner.
I have often found people unfamiliar with the T-SQL are often thrown by the FOR XML PATH clause and what it does and so have written this
to the FOR XML PATH's basic elements.
Follow through this tutorial by running each statement and comparing with previous statements to see the effect of each change.
--these sql statments can be run on the AdventureWorks database in 2005
*/
--This statment brings back suppliers into XML with a default - <row> for each entry.
--Note: The syntax for creating an attribute is an '@' in the column name and a separate node is '/' in column name.
SELECT TOP 10 PERCENT
pv.VendorID AS '@VendorID',
pv.AccountNumber AS '@AccountNumber',
pv.Name,
pv.ActiveFlag AS 'Details/@ActiveFlag',
pv.CreditRating AS 'Details/CreditRating',
pv.PreferredVendorStatus AS 'Details/PreferredVendorStatus'
FROM
Purchasing.Vendor pv
FOR XML PATH
--As the xml is written out in a linear fashion, any attributes for a node must be written before the nodes.
--otherwise you get the error 'Attribute-centric column '[ColumnName]' must not come after a non-attribute-centric sibling in XML hierarchy in FOR XML PATH.'
--See the error when the following is run
SELECT TOP 10 PERCENT
pv.VendorID AS '@VendorID',
pv.AccountNumber AS '@AccountNumber',
pv.Name,
pv.CreditRating AS 'Details/CreditRating',
pv.ActiveFlag AS 'Details/@ActiveFlag',
pv.PreferredVendorStatus AS 'Details/PreferredVendorStatus'
FROM
Purchasing.Vendor pv
FOR XML PATH
--Nodes should be kept together,
--Otherwise they will appear twice in the list. See below
SELECT TOP 10 PERCENT
pv.VendorID AS '@VendorID',
pv.AccountNumber AS '@AccountNumber',
pv.PreferredVendorStatus AS 'Details/PreferredVendorStatus',
pv.Name,
pv.ActiveFlag AS 'Details/@ActiveFlag',
pv.CreditRating AS 'Details/CreditRating'
FROM
Purchasing.Vendor pv
FOR XML PATH
--This brings back vendors in <Vendor> nodes instead of <row> nodes
--Before: FOR xml path
--After: FOR xml path ('Vendor')
SELECT TOP 10 PERCENT
pv.VendorID AS '@VendorID',
pv.AccountNumber AS '@AccountNumber',
pv.Name,
pv.ActiveFlag AS 'Details/@ActiveFlag',
pv.CreditRating AS 'Details/CreditRating',
pv.PreferredVendorStatus AS 'Details/PreferredVendorStatus'
FROM
Purchasing.Vendor pv
FOR XML PATH('Vendor')
--This change wraps all nodes in a root <Vendors> node
--Before: FOR xml path ('Vendor')
--After: FOR xml path ('Vendor'), root('Vendors')
SELECT TOP 10 PERCENT
pv.VendorID AS '@VendorID',
pv.AccountNumber AS '@AccountNumber',
pv.Name,
pv.ActiveFlag AS 'Details/@ActiveFlag',
pv.CreditRating AS 'Details/CreditRating',
pv.PreferredVendorStatus AS 'Details/PreferredVendorStatus'
FROM
Purchasing.Vendor pv
FOR XML PATH('Vendor'), root('Vendors')
--This demonstrates adding products to the query the wrong way,
--involving a join.
--Note that the <Vendor> is repeated for each <Product>
SELECT TOP 10 PERCENT
pv.VendorID AS '@VendorID',
pv.AccountNumber AS '@AccountNumber',
pv.Name,
pv.ActiveFlag AS 'Details/@ActiveFlag',
pv.CreditRating AS 'Details/CreditRating',
pv.PreferredVendorStatus AS 'Details/PreferredVendorStatus',
pp.ProductID AS 'Product/@ProductID',
pp.Name AS 'Product/Name',
pp.ProductNumber AS 'Product/ProductNumber'
FROM
Purchasing.Vendor pv
INNER JOIN
Purchasing.ProductVendor ppv ON ppv.VendorID = pv.VendorID
INNER JOIN
Production.Product pp ON pp.ProductID = ppv.ProductID
ORDER BY pv.VendorID, pp.ProductID
FOR XML PATH('Vendor')
--As repeating the <Vendor> node for each <Product> is far less than ideal,
--we can add products in a nested select.
SELECT TOP 10 PERCENT
pv.VendorID AS '@VendorID',
pv.AccountNumber AS '@AccountNumber',
pv.Name,
pv.ActiveFlag AS 'Details/@ActiveFlag',
pv.CreditRating AS 'Details/CreditRating',
pv.PreferredVendorStatus AS 'Details/PreferredVendorStatus',
(
SELECT
pp.ProductID AS '@ProductID',
pp.Name,
pp.ProductNumber
FROM
Purchasing.ProductVendor ppv
INNER JOIN
Production.Product pp ON pp.ProductID = ppv.ProductID
WHERE
ppv.VendorID = pv.VendorID
FOR XML PATH('Product')
) AS Products
FROM
Purchasing.Vendor pv
ORDER BY pv.VendorID
FOR XML PATH('Vendor')
--Eeeeuugh, but thats a nasty looking products node
--A little change will clear that up though.
--Before: FOR xml path('Product')
--After: FOR xml path('Product'), type
SELECT TOP 10 PERCENT
pv.VendorID AS '@VendorID',
pv.AccountNumber AS '@AccountNumber',
pv.Name,
pv.ActiveFlag AS 'Details/@ActiveFlag',
pv.CreditRating AS 'Details/CreditRating',
pv.PreferredVendorStatus AS 'Details/PreferredVendorStatus',
(
SELECT
pp.ProductID AS '@ProductID',
pp.Name,
pp.ProductNumber
FROM
Purchasing.ProductVendor ppv
INNER JOIN
Production.Product pp ON pp.ProductID = ppv.ProductID
WHERE
ppv.VendorID = pv.VendorID
FOR XML PATH('Product'), type
) AS Products
FROM
Purchasing.Vendor pv
ORDER BY pv.VendorID
FOR XML PATH('Vendor')
--We are now done with this little primer.
--I hope it has been useful to you.
Sreedhar, Thanks for your comment. I've uploaded another sample
that I hope will answer your question. Also check: