<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>SQL Fool &#187; concatenation</title>
	<atom:link href="http://sqlfool.com/tag/concatenation/feed/" rel="self" type="application/rss+xml" />
	<link>http://sqlfool.com</link>
	<description>Self-Professed SQL Scripting Junkie!</description>
	<lastBuildDate>Wed, 02 May 2012 21:25:28 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	
		<item>
		<title>Row Concatenation in T-SQL</title>
		<link>http://sqlfool.com/2008/11/row-concatenation-in-t-sql/</link>
		<comments>http://sqlfool.com/2008/11/row-concatenation-in-t-sql/#comments</comments>
		<pubDate>Fri, 07 Nov 2008 04:12:48 +0000</pubDate>
		<dc:creator>Michelle Ufford</dc:creator>
				<category><![CDATA[Performance & Tuning]]></category>
		<category><![CDATA[SQL Tips]]></category>
		<category><![CDATA[T-SQL Scripts]]></category>
		<category><![CDATA[concatenation]]></category>
		<category><![CDATA[TSQL]]></category>

		<guid isPermaLink="false">http://sqlfool.com/?p=115</guid>
		<description><![CDATA[Have you ever needed to create a comma-delimited list of related records, grouped by a parent record? This is one of those business requirements that just happens to pop up every so often. For example, turning this: AuthorID&#160;&#160;&#160; BookName 1 &#8220;Great Expectations&#8221; 1 &#8220;David Copperfield&#8221; 1 &#8220;Bleak House&#8221; 2 &#8220;Divine Comedy&#8221; 3 &#8220;Pride and Prejudice&#8221; [...]]]></description>
			<content:encoded><![CDATA[<p>Have you ever needed to create a comma-delimited list of related records, grouped by a parent record?  This is one of those business requirements that just happens to pop up every so often.  For example, turning this:</p>
<table border="0">
<tr>
<td>AuthorID&nbsp;&nbsp;&nbsp;</td>
<td>BookName</td>
</tr>
<tr>
<td>1</td>
<td>&#8220;Great Expectations&#8221;</td>
</tr>
<tr>
<td>1</td>
<td>&#8220;David Copperfield&#8221;</td>
</tr>
<tr>
<td>1</td>
<td>&#8220;Bleak House&#8221;</td>
</tr>
<tr>
<td>2</td>
<td>&#8220;Divine Comedy&#8221;</td>
</tr>
<tr>
<td>3</td>
<td>&#8220;Pride and Prejudice&#8221;</td>
</tr>
<tr>
<td>3</td>
<td>&#8220;Mansfield Park&#8221;</td>
</tr>
<tr>
<td>&nbsp;</td>
<td>&nbsp;</td>
</tr>
</table>
<p>into this:</p>
<table border="0">
<tr>
<td>AuthorID&nbsp;&nbsp;&nbsp;</td>
<td>ListOfBooks</td>
</tr>
<tr>
<td>1</td>
<td>&#8220;Great Expectations&#8221;, &#8220;David Copperfield&#8221;, &#8220;Bleak House&#8221;</td>
</tr>
<tr>
<td>2</td>
<td>&#8220;Divine Comedy&#8221;</td>
</tr>
<tr>
<td>3</td>
<td>&#8220;Pride and Prejudice&#8221;, &#8220;Mansfield Park&#8221;</td>
</tr>
<tr>
<td>&nbsp;</td>
<td>&nbsp;</td>
</tr>
</table>
<p>There&#8217;s a handful of ways you can handle this, especially when dealing with small data sets.  However, the problem becomes a bit more tricky when dealing with large record sets (i.e. hundreds of thousands or even millions of records).  My first attempt at a solution used a CTE (common table expression).  It did the job but was very slow.  Looking for a better solution, I discovered the XML method.</p>
<p>To give you a quick example:</p>

<div class="wp_syntax"><div class="code"><pre class="tsql" style="font-family:monospace;"><span style="color: #008080;">/* Create a table variable to play with */</span>
<span style="color: #0000FF;">Declare</span> @myTable <span style="color: #0000FF;">Table</span> 
    <span style="color: #808080;">&#40;</span>customerID <span style="color: #0000FF;">int</span>, textData <span style="color: #0000FF;">varchar</span><span style="color: #808080;">&#40;</span><span style="color: #000;">10</span><span style="color: #808080;">&#41;</span><span style="color: #808080;">&#41;</span>;
&nbsp;
<span style="color: #008080;">/* Populate some test rescords */</span>
<span style="color: #0000FF;">Insert</span> <span style="color: #0000FF;">Into</span> @myTable
<span style="color: #0000FF;">Select</span> <span style="color: #000;">1</span>, <span style="color: #FF0000;">'abc'</span> <span style="color: #0000FF;">Union</span> All
<span style="color: #0000FF;">Select</span> <span style="color: #000;">1</span>, <span style="color: #FF0000;">'def'</span> <span style="color: #0000FF;">Union</span> All
<span style="color: #0000FF;">Select</span> <span style="color: #000;">2</span>, <span style="color: #FF0000;">'uvw'</span> <span style="color: #0000FF;">Union</span> All
<span style="color: #0000FF;">Select</span> <span style="color: #000;">2</span>, <span style="color: #FF0000;">'xyz'</span>
&nbsp;
<span style="color: #008080;">/* Just take a look at the data
   before we format it */</span>
<span style="color: #0000FF;">Select</span> <span style="color: #808080;">*</span> <span style="color: #0000FF;">From</span> @myTable;
&nbsp;
<span style="color: #008080;">/* Let's take a look at what
   For XML Raw will return 
   for us */</span>
<span style="color: #0000FF;">Select</span> textData
<span style="color: #0000FF;">From</span> @myTable
<span style="color: #0000FF;">Where</span> customerID <span style="color: #808080;">=</span> <span style="color: #000;">1</span>
<span style="color: #0000FF;">Order</span> <span style="color: #0000FF;">By</span> textData
<span style="color: #0000FF;">For</span> XML Raw;
&nbsp;
<span style="color: #008080;">/* Now consolidate the data, using
   the For XML Raw option to 
   concatenate the textData column */</span>
<span style="color: #0000FF;">Select</span> customerID
        , <span style="color: #FF00FF;">Replace</span><span style="color: #808080;">&#40;</span> <span style="color: #FF00FF;">Replace</span><span style="color: #808080;">&#40;</span> <span style="color: #FF00FF;">Replace</span><span style="color: #808080;">&#40;</span>
            <span style="color: #808080;">&#40;</span>   <span style="color: #0000FF;">Select</span> textData
                <span style="color: #0000FF;">From</span> @myTable <span style="color: #0000FF;">As</span> a
                <span style="color: #0000FF;">Where</span> a.<span style="color: #202020;">customerID</span> <span style="color: #808080;">=</span> b.<span style="color: #202020;">customerID</span>
                <span style="color: #0000FF;">Order</span> <span style="color: #0000FF;">By</span> textData
                <span style="color: #0000FF;">For</span> XML Raw<span style="color: #808080;">&#41;</span>
                , <span style="color: #FF0000;">'&quot;/&gt;&lt;row textData=&quot;'</span>, <span style="color: #FF0000;">', '</span><span style="color: #808080;">&#41;</span>
                , <span style="color: #FF0000;">'&lt;row textData=&quot;'</span>, <span style="color: #FF0000;">''</span><span style="color: #808080;">&#41;</span>
                , <span style="color: #FF0000;">'&quot;/&gt;'</span>, <span style="color: #FF0000;">''</span><span style="color: #808080;">&#41;</span>
            <span style="color: #0000FF;">As</span> <span style="color: #FF0000;">'textData'</span>
<span style="color: #0000FF;">From</span> @myTable b
<span style="color: #0000FF;">Group</span> <span style="color: #0000FF;">By</span> customerID;</pre></div></div>

<p>This has become my default method for handling this report requirement.  While discussing this with a colleague, he mentioned using an approach that was similar in design but used Cross Apply on the XML.  Wanting to see which performed better, I ran the two following queries in the AdventureWorks sample database (2008):</p>

<div class="wp_syntax"><div class="code"><pre class="tsql" style="font-family:monospace;"><span style="color: #008080;">/* Method 1 */</span>
<span style="color: #0000FF;">Select</span> ProductsOrdered
    , <span style="color: #FF00FF;">Count</span><span style="color: #808080;">&#40;</span><span style="color: #808080;">*</span><span style="color: #808080;">&#41;</span> <span style="color: #0000FF;">As</span> <span style="color: #FF0000;">'salesOrders'</span>
<span style="color: #0000FF;">From</span> <span style="color: #808080;">&#40;</span>
    <span style="color: #0000FF;">Select</span> SalesOrderID
            , <span style="color: #FF00FF;">Replace</span><span style="color: #808080;">&#40;</span> <span style="color: #FF00FF;">Replace</span><span style="color: #808080;">&#40;</span> <span style="color: #FF00FF;">Replace</span><span style="color: #808080;">&#40;</span>
                <span style="color: #808080;">&#40;</span>   <span style="color: #0000FF;">Select</span> <span style="color: #0000FF;">Top</span> <span style="color: #000;">10</span> ProductID
                    <span style="color: #0000FF;">From</span> Sales.<span style="color: #202020;">SalesOrderDetail</span> <span style="color: #0000FF;">As</span> sod <span style="color: #0000FF;">With</span> <span style="color: #808080;">&#40;</span>NoLock<span style="color: #808080;">&#41;</span>
                    <span style="color: #0000FF;">Where</span> soh.<span style="color: #202020;">SalesOrderID</span> <span style="color: #808080;">=</span> sod.<span style="color: #202020;">SalesOrderID</span>
                    <span style="color: #0000FF;">Order</span> <span style="color: #0000FF;">By</span> ProductID
                    <span style="color: #0000FF;">For</span> XML Raw<span style="color: #808080;">&#41;</span>
                    , <span style="color: #FF0000;">'&quot;/&gt;&lt;row ProductID=&quot;'</span>, <span style="color: #FF0000;">', '</span><span style="color: #808080;">&#41;</span>
                    , <span style="color: #FF0000;">'&lt;row ProductID=&quot;'</span>, <span style="color: #FF0000;">''</span><span style="color: #808080;">&#41;</span>
                    , <span style="color: #FF0000;">'&quot;/&gt;'</span>, <span style="color: #FF0000;">''</span><span style="color: #808080;">&#41;</span>
                <span style="color: #0000FF;">As</span> <span style="color: #FF0000;">'ProductsOrdered'</span>
    <span style="color: #0000FF;">From</span> Sales.<span style="color: #202020;">SalesOrderHeader</span> <span style="color: #0000FF;">As</span> soh <span style="color: #0000FF;">With</span> <span style="color: #808080;">&#40;</span>NoLock<span style="color: #808080;">&#41;</span>
<span style="color: #808080;">&#41;</span> x
<span style="color: #0000FF;">Group</span> <span style="color: #0000FF;">By</span> ProductsOrdered
<span style="color: #0000FF;">Order</span> <span style="color: #0000FF;">By</span> <span style="color: #FF00FF;">Count</span><span style="color: #808080;">&#40;</span><span style="color: #808080;">*</span><span style="color: #808080;">&#41;</span> <span style="color: #0000FF;">Desc</span>
<span style="color: #0000FF;">Option</span> <span style="color: #808080;">&#40;</span>MaxDop <span style="color: #000;">1</span><span style="color: #808080;">&#41;</span>;
&nbsp;
<span style="color: #008080;">/* Method 2 */</span>
<span style="color: #0000FF;">Select</span> ProductsOrdered
    , <span style="color: #FF00FF;">Count</span><span style="color: #808080;">&#40;</span><span style="color: #808080;">*</span><span style="color: #808080;">&#41;</span> <span style="color: #0000FF;">As</span> <span style="color: #FF0000;">'salesOrders'</span>
<span style="color: #0000FF;">From</span> <span style="color: #808080;">&#40;</span>
    <span style="color: #0000FF;">Select</span> SalesOrderID
        , <span style="color: #FF00FF;">SubString</span><span style="color: #808080;">&#40;</span>ProductsOrdered, <span style="color: #000;">1</span>, <span style="color: #FF00FF;">Len</span><span style="color: #808080;">&#40;</span>ProductsOrdered<span style="color: #808080;">&#41;</span> <span style="color: #808080;">-</span> <span style="color: #000;">1</span><span style="color: #808080;">&#41;</span> 
              <span style="color: #0000FF;">As</span> <span style="color: #FF0000;">'ProductsOrdered'</span>
    <span style="color: #0000FF;">From</span> Sales.<span style="color: #202020;">SalesOrderHeader</span> <span style="color: #0000FF;">As</span> soh <span style="color: #0000FF;">With</span> <span style="color: #808080;">&#40;</span>NoLock<span style="color: #808080;">&#41;</span>
    Cross Apply <span style="color: #808080;">&#40;</span>
                    <span style="color: #0000FF;">Select</span> <span style="color: #0000FF;">Top</span> <span style="color: #000;">10</span> 
                        <span style="color: #0000FF;">Cast</span><span style="color: #808080;">&#40;</span>ProductID <span style="color: #0000FF;">As</span> <span style="color: #0000FF;">varchar</span><span style="color: #808080;">&#40;</span><span style="color: #000;">10</span><span style="color: #808080;">&#41;</span><span style="color: #808080;">&#41;</span> <span style="color: #808080;">+</span> <span style="color: #FF0000;">', '</span>
                    <span style="color: #0000FF;">From</span> Sales.<span style="color: #202020;">SalesOrderDetail</span> <span style="color: #0000FF;">As</span> sod <span style="color: #0000FF;">With</span> <span style="color: #808080;">&#40;</span>NoLock<span style="color: #808080;">&#41;</span>
                    <span style="color: #0000FF;">Where</span> sod.<span style="color: #202020;">SalesOrderID</span> <span style="color: #808080;">=</span> soh.<span style="color: #202020;">SalesOrderID</span>
                    <span style="color: #0000FF;">Order</span> <span style="color: #0000FF;">By</span> ProductID
                    <span style="color: #0000FF;">For</span> XML <span style="color: #0000FF;">Path</span><span style="color: #808080;">&#40;</span><span style="color: #FF0000;">''</span><span style="color: #808080;">&#41;</span>
                <span style="color: #808080;">&#41;</span> X<span style="color: #808080;">&#40;</span>ProductsOrdered<span style="color: #808080;">&#41;</span>
<span style="color: #808080;">&#41;</span> x
<span style="color: #0000FF;">Group</span> <span style="color: #0000FF;">By</span> ProductsOrdered
<span style="color: #0000FF;">Order</span> <span style="color: #0000FF;">By</span> <span style="color: #FF00FF;">Count</span><span style="color: #808080;">&#40;</span><span style="color: #808080;">*</span><span style="color: #808080;">&#41;</span> <span style="color: #0000FF;">Desc</span>;</pre></div></div>

<p>Here&#8217;s the results: (click to enlarge)</p>
<div class="wp-caption alignnone" style="width: 677px"><a href="http://www.sqlfool.com/wp-content/uploads/RowConcatenationComparison.JPG"><img alt="Row Concatenation Comparison" src="http://www.sqlfool.com/wp-content/uploads/RowConcatenationComparison.JPG" title="Row Concatenation Comparison" width="400" height="300" /></a><p class="wp-caption-text">Row Concatenation Comparison</p></div>
<p>As evidenced in the image above, the first method has slightly higher CPU and double the duration.  The 2nd method had almost double the writes and significantly more reads.</p>
<p>I was hoping for a clear winner, but apparently each method has its benefits.  I&#8217;ll probably continue to stick with my original, more resource-friendly method (Method 1), unless someone suggests a better solution.  <img src='http://sqlfool.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
]]></content:encoded>
			<wfw:commentRss>http://sqlfool.com/2008/11/row-concatenation-in-t-sql/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>

