SQL For XML

You might also like

Download as docx, pdf, or txt
Download as docx, pdf, or txt
You are on page 1of 3

http://davidduffett.

net/post/5334646215/get-a-comma-separated-list-of-values-in-sql-with

Get a comma separated list of values in SQL with FOR XML


Heres a hidden trick I found recently in SQL Server using a combination of its built-in XML
capabilities, and the STUFF statement.
Ive had some cases where Ive needed to concatenate values of a particular column in a table into a
comma-separated string. For example, the models that a part fits:

As this came up a lot, we wrote a CLR function in C#, imported this into SQL Server and created a
User Defined Function (UDF) to ease concatenation of string values from rows in a table. I had a few
major problems with this approach:
1.

The code is unnecessarily complex for such a simple requirement, mainly due to the
plumbing code required to make SQL CLR functions
2.
We now have to add another process to our deployment, installing this CLR function in SQL
Server
3.
People now have to know how to do this. This is a problem - how often do you actually write
CLR functions and reference them in SQL??
I was overjoyed to find a way of achieving this that is more efficient and involves purely SQL! In can
be achieved using the SQL STUFF statement, and SQL Servers built-in XML capabilities.
Say I have the following tables (very simplistic):
CREATE TABLE [dbo].[Product](
[ProductId] [bigint] NOT NULL,
[Name] [varchar](255) NOT NULL
)
CREATE TABLE [dbo].[ModelProductFits](
[ProductId] [bigint] NOT NULL,
[ModelThisFits] [varchar](50) NOT NULL,
)
With the following values:
----------------------------| ProductId | ModelThisFits |
|---------------------------|
| 12345
| A3666
|
| 12345
| A3667
|
| 12345
| A8999
|
----------------------------And I want to output the following:
----------------------------------| ProductId | Models
|
|---------------------------------|
| 12345
| A3666,A3667,A8999
|
-----------------------------------

How can we achieve this just in T-SQL?

1. Get XML element string with FOR XML


Adding FOR XML PATH to the end of a query allows you to output the results of the query as XML
elements, with the element name contained in the PATH argument. For example, if we were to run
the following statement:
SELECT ',' + ModelThisFits
FROM ModelProductFits
ORDER BY ModelThisFits
FOR XML PATH('Model')
We would receive the following XML string:
,A3666,A3667,A8999
By passing in a blank string (FOR XML PATH()), we get the following instead:
,A3666,A3667,A8999

2. Remove leading comma with STUFF


The STUFF statement literally "stuffs one string into another, replacing characters within the first
string. We, however, are using it simply to remove the first character of the resultant list of values.
The parameters of STUFF are:
1.
The string to be stuffed (in our case the full list of models with a leading comma)
2.
The location to start deleting and inserting characters (1, were stuffing into a blank string)
3.
The number of characters to delete (1, being the leading comma)
SELECT STUFF
(
SELECT ',' + ModelThisFits
FROM ModelProductFits
ORDER BY ModelThisFits
FOR XML PATH('Model')
), 1, 1, ''
So we end up with:
A3666,A3667,A8999

3. Join on Product to get full list


Next we just join this on the list of products in the Product table, to get a list of product IDs with
models they fit!
SELECT
P.ProductId,
STUFF
(
(
SELECT ',' + ModelThisFits
FROM ModelProductFits M
WHERE M.ProductId = P.ProductId
ORDER BY ModelThisFits
FOR XML PATH('')
), 1, 1, ''
) AS Models
FROM
Product P
And we have our result:

----------------------------------| ProductId | Models


|
|---------------------------------|
| 12345
| A3666,A3667,A8999
|
----------------------------------Simple.
UPDATE
Thanks to denisvaleevs comments on this post 3 years ago, Ive remembered an issue that can be
encountered by using this technique - truncation of the resulting value. Id recommend you cast the
resulting XML to varchar(max), by doing the following:
SELECT
P.ProductId,
STUFF
(
(
SELECT ',' + ModelThisFits
FROM ModelProductFits M
WHERE M.ProductId = P.ProductId
ORDER BY ModelThisFits
FOR XML PATH(''), type
).value('.', 'varchar(max)'), 1, 1, ''
) AS Models
FROM
Product P

SELECT Item,STUFF
(
(SELECT ',' + Color
FROM [dbo].[Inventory2]
ORDER BY Color
FOR XML PATH('')),1,1,'') AS CSV
FROM [dbo].[Inventory2]

You might also like