<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://sqlblog.com/utility/FeedStylesheets/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/"><channel><title>Search results matching tags 'T-SQL Tuesday', 'for xml', and 'aggregates'</title><link>http://sqlblog.com/search/SearchResults.aspx?o=DateDescending&amp;tag=T-SQL+Tuesday,for+xml,aggregates&amp;orTags=0</link><description>Search results matching tags 'T-SQL Tuesday', 'for xml', and 'aggregates'</description><dc:language>en-US</dc:language><generator>CommunityServer 2.1 SP2 (Build: 61129.1)</generator><item><title>T-SQL Tuesday # 16 : This is not the aggregate you're looking for</title><link>http://sqlblog.com/blogs/aaron_bertrand/archive/2011/03/08/t-sql-tuesday-16-this-is-not-the-aggregate-you-re-looking-for.aspx</link><pubDate>Tue, 08 Mar 2011 20:49:00 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:33956</guid><dc:creator>AaronBertrand</dc:creator><description>
&lt;table cellpadding="10" cellspacing="0"&gt;

&lt;tr&gt;
&lt;td&gt;&lt;a href="http://blogs.lessthandot.com/index.php/DataMgmt/DBProgramming/come-one-come-all-to" title="http://blogs.lessthandot.com/index.php/DataMgmt/DBProgramming/come-one-come-all-to" target="_blank"&gt;&lt;img src="http://sqlblog.com/files/folders/30073/download.aspx" align="left" border="0" height="154" hspace="22" width="154"&gt;&lt;/a&gt;&lt;/td&gt;

&lt;td&gt;&amp;nbsp;
&lt;br&gt;This week, T-SQL Tuesday is being hosted by Jes Borland (&lt;a href="http://blogs.lessthandot.com/index.php/DataMgmt/?disp=user&amp;amp;user_ID=420" title="http://blogs.lessthandot.com/index.php/DataMgmt/?disp=user&amp;amp;user_ID=420" target="_blank"&gt;blog&lt;/a&gt; | &lt;a href="http://twitter.com/grrl_geek" title="http://twitter.com/grrl_geek" target="_blank"&gt;twitter&lt;/a&gt;), and the theme is "&lt;a href="http://blogs.lessthandot.com/index.php/DataMgmt/DBProgramming/come-one-come-all-to" title="http://blogs.lessthandot.com/index.php/DataMgmt/DBProgramming/come-one-come-all-to" target="_blank"&gt;Aggregate Functions&lt;/a&gt;." &lt;br&gt;

&lt;p&gt;When people think of aggregates, they tend to think of MAX(), SUM() and COUNT(). And occasionally, less common functions such as AVG() and STDEV(). I thought I would write a quick post about a different type of aggregate: string concatenation. Even going back to my classic ASP days, one of the more common questions out in the community has been, "how do I turn a column into a comma-separated list?"&lt;/p&gt;

&lt;p&gt;Over the years, I've used a variety of approaches to solve this problem. For this post I wanted to compare a few different methods, sticking to pure T-SQL and ignoring external solutions such as SQLCLR for brevity. I'm also going to use inline code instead of bothering to put anything into UDFs, so it's clear that function calling etc. is not interfering with the results. I'm sure there is nothing you haven't seen before, but having spotted several recent discussions where people profess things like, "a while loop is more efficient than a cursor," I thought it would be useful to take a quick glance at the performance of each of the common approaches.&lt;br&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;

&lt;p&gt;&lt;font size="4"&gt;&lt;b&gt;The Goal&lt;/b&gt;&lt;/font&gt;&lt;/p&gt;

&lt;p&gt;Let's make the goal kind of silly: we want all of the names from msdb.sys.objects, but rather than multiple rows in a single column, we want the names in a single tuple, separated by columns. So instead of:&lt;/p&gt;

&lt;p style="margin-left:40px;"&gt;&lt;img src="http://sqlblog.com/files/folders/33954/download.aspx" border="1" height="116" width="213"&gt;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;We want this:&lt;/p&gt;

&lt;p style="margin-left:40px;"&gt;&lt;img src="http://sqlblog.com/files/folders/33955/download.aspx" border="1" height="47" width="642"&gt;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;A couple of things to note: we don't need to worry about NULL values in our concatenation, so there will be no special handling for that case, though it may be necessary in your scenario. I also don't need to worry about embedded commas in the data, because I know that none of my objects in MSDB are named using commas.&lt;br&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;b&gt;&lt;font size="4"&gt;The Setup&lt;/font&gt;&lt;/b&gt; &lt;/p&gt;

&lt;p&gt;I placed this code at the top of my script. I wanted a table variable to hold the object names, and some variables that would be re-used through the various string concatenation methods. &lt;br&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;table bgcolor="#eeeeee" cellpadding="0" cellspacing="0"&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;pre style="padding:10px 20px;font-size:12px;font-family:consolas,lucida console,courier new,courier;-moz-background-inline-policy:continuous;"&gt;&lt;font color="blue"&gt;USE &lt;/font&gt;&lt;font color="black"&gt;[msdb]&lt;/font&gt;&lt;font color="gray"&gt;;&lt;br&gt;&lt;/font&gt;&lt;font color="black"&gt;&lt;br&gt;&lt;/font&gt;&lt;font color="blue"&gt;SET NOCOUNT ON&lt;/font&gt;&lt;font color="gray"&gt;;&lt;br&gt;&lt;br&gt;&lt;/font&gt;&lt;font color="blue"&gt;DECLARE &lt;/font&gt;&lt;font color="#434343"&gt;@foo &lt;/font&gt;&lt;font color="blue"&gt;TABLE&lt;br&gt;&lt;/font&gt;&lt;font color="gray"&gt;(&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="black"&gt;id &lt;/font&gt;&lt;font color="blue"&gt;  INT IDENTITY&lt;/font&gt;&lt;font color="gray"&gt;(&lt;/font&gt;&lt;font color="black"&gt;1&lt;/font&gt;&lt;font color="gray"&gt;,&lt;/font&gt;&lt;font color="black"&gt;1&lt;/font&gt;&lt;font color="gray"&gt;)&lt;/font&gt;&lt;font color="gray"&gt; &lt;/font&gt;&lt;font color="blue"&gt;PRIMARY KEY&lt;/font&gt;&lt;font color="gray"&gt;,&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="black"&gt;name &lt;/font&gt;&lt;font color="blue"&gt;NVARCHAR&lt;/font&gt;&lt;font color="gray"&gt;(&lt;/font&gt;&lt;font color="black"&gt;257&lt;/font&gt;&lt;font color="gray"&gt;)&lt;/font&gt;&lt;font color="blue"&gt;&lt;br&gt;&lt;/font&gt;&lt;font color="gray"&gt;);&lt;br&gt;&lt;br&gt;&lt;/font&gt;&lt;font color="blue"&gt;INSERT &lt;/font&gt;&lt;font color="#434343"&gt;@foo&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="blue"&gt;SELECT &lt;/font&gt;&lt;font color="blue"&gt;DISTINCT&lt;/font&gt;&lt;font color="blue"&gt; &lt;/font&gt;&lt;font color="black"&gt;n =&lt;/font&gt;&lt;font color="blue"&gt; &lt;/font&gt;&lt;font&gt;&lt;font color="magenta"&gt;OBJECT_SCHEMA_NAME&lt;/font&gt;&lt;/font&gt;&lt;font color="gray"&gt;(&lt;/font&gt;&lt;font color="black"&gt;[object_id]&lt;/font&gt;&lt;font color="gray"&gt;) + &lt;/font&gt;&lt;font color="red"&gt;'.' &lt;/font&gt;&lt;font color="gray"&gt;+ &lt;/font&gt;&lt;font color="black"&gt;name&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="blue"&gt;FROM &lt;/font&gt;&lt;font color="black"&gt;&lt;font color="#009900"&gt;sys&lt;/font&gt;.&lt;font color="#009900"&gt;objects&lt;/font&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="blue"&gt;ORDER BY &lt;/font&gt;&lt;font color="black"&gt;n&lt;/font&gt;&lt;font color="gray"&gt;;&lt;br&gt;&lt;br&gt;&lt;/font&gt;&lt;font color="blue"&gt;DECLARE&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="#434343"&gt;@s&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;font color="blue"&gt;NVARCHAR&lt;/font&gt;&lt;font color="gray"&gt;(&lt;/font&gt;&lt;font color="magenta"&gt;MAX&lt;/font&gt;&lt;font color="gray"&gt;),&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="#434343"&gt;@n&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;font color="blue"&gt;NVARCHAR&lt;/font&gt;&lt;font color="gray"&gt;(&lt;/font&gt;&lt;font color="black"&gt;257&lt;/font&gt;&lt;font color="gray"&gt;)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="#434343"&gt;@loop&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;font color="blue"&gt;INT &lt;/font&gt;&lt;font&gt;&lt;font color="black"&gt;=&lt;/font&gt;&lt;/font&gt;&lt;font color="blue"&gt; &lt;/font&gt;&lt;font color="black"&gt;0&lt;/font&gt;&lt;font color="gray"&gt;,&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="#434343"&gt;@count&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="blue"&gt;INT &lt;/font&gt;&lt;font&gt;&lt;font color="black"&gt;=&lt;/font&gt;&lt;/font&gt;&lt;font color="blue"&gt; &lt;/font&gt;&lt;font color="gray"&gt;(&lt;/font&gt;&lt;font color="blue"&gt;SELECT &lt;/font&gt;&lt;font color="magenta"&gt;COUNT&lt;/font&gt;&lt;font color="gray"&gt;(*) &lt;/font&gt;&lt;font color="blue"&gt;FROM &lt;/font&gt;&lt;font color="#434343"&gt;@foo&lt;/font&gt;&lt;font color="gray"&gt;);&lt;/font&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;br&gt;&lt;b&gt;&lt;font size="4"&gt;Approach #1a : An "Evil" Cursor &lt;/font&gt;&lt;/b&gt;&lt;br&gt;&lt;/p&gt;

&lt;p&gt;Very early on in my career, my approach would have been one of brute force: use a cursor. These days, when I see people compare a cursor to a while loop, I see them making a very unfair comparison; they use the default options for a cursor, which can be fairly heavyweight depending on concurrency and the rest of your workload. And I'll admit, in those early days, I often just blurted out "DECLARE c CURSOR FOR..." without any thoughts to options that I should be setting, such as READ_ONLY, FORWARD_ONLY, LOCAL, etc. I don't want this to blossom into a lesson on cursors and default settings vs. optimal settings (why GLOBAL is still the default, I'll never understand), but I will tell you that I now always use the following syntax:&lt;br&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;table bgcolor="#eeeeee" cellpadding="0" cellspacing="0"&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;pre style="padding:10px 20px;font-size:12px;font-family:consolas,lucida console,courier new,courier;-moz-background-inline-policy:continuous;"&gt;&lt;font color="blue"&gt;DECLARE &lt;/font&gt;&lt;font color="black"&gt;cursor_name &lt;/font&gt;&lt;font color="blue"&gt;CURSOR&lt;br&gt;    LOCAL STATIC FORWARD_ONLY READ_ONLY&lt;br&gt;&lt;/font&gt;&lt;font color="black"&gt;&lt;/font&gt;&lt;font color="blue"&gt;&lt;font color="black"&gt;    &lt;/font&gt;FOR &lt;/font&gt;&lt;font color="black"&gt;...&lt;/font&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;/blockquote&gt;

While it's true that FAST_FORWARD is essentially shorthand for FORWARD_ONLY and READ_ONLY, if you try to specify STATIC FAST_FORWARD, you get this error:

&lt;blockquote&gt;
&lt;table bgcolor="#eeeeee" cellpadding="0" cellspacing="0"&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;pre style="padding:10px 20px;font-size:12px;font-family:consolas,lucida console,courier new,courier;-moz-background-inline-policy:continuous;"&gt;&lt;font color="red"&gt;Msg 1048, Level 15, State 1, Line 2&lt;br&gt;Conflicting cursor options STATIC and FAST_FORWARD.&lt;/font&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;/blockquote&gt;

&lt;p&gt;Hugo Kornelis does a great job of comparing the performance of all of the cursor options in &lt;a href="http://sqlblog.com/blogs/hugo_kornelis/archive/2007/11/21/curious-cursor-optimization-options.aspx" title="http://sqlblog.com/blogs/hugo_kornelis/archive/2007/11/21/curious-cursor-optimization-options.aspx" target="_blank"&gt;this blog post&lt;/a&gt;. The two most interesting things I got from this article: (1) STATIC is faster than FAST_FORWARD, and (2) the default options will always result in the slowest possible performance (the latter I already knew, which is why it always irked me when people used the default options to show how slow cursors are). In spite of his assurances that there is no difference between LOCAL and GLOBAL or whether or not you use READ_ONLY, I am probably going to keep using them for documentation and future-proofing reasons.&lt;br&gt;&lt;/p&gt;

&lt;p&gt;That all said, here is how many people would use a default cursor to achieve the desired result:&lt;/p&gt;

&lt;blockquote&gt;
&lt;table bgcolor="#eeeeee" cellpadding="0" cellspacing="0"&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;pre style="padding:10px 20px;font-size:12px;font-family:consolas,lucida console,courier new,courier;-moz-background-inline-policy:continuous;"&gt;&lt;font color="blue"&gt;DECLARE &lt;/font&gt;&lt;font color="black"&gt;c &lt;/font&gt;&lt;font color="blue"&gt;CURSOR&lt;br&gt;&lt;/font&gt;&lt;font color="black"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;font color="blue"&gt;FOR &lt;br&gt;    SELECT &lt;/font&gt;&lt;font color="black"&gt;name &lt;/font&gt;&lt;font color="blue"&gt;FROM &lt;/font&gt;&lt;font color="#434343"&gt;@foo &lt;/font&gt;&lt;font color="blue"&gt;ORDER BY &lt;/font&gt;&lt;font color="black"&gt;name&lt;/font&gt;&lt;font color="gray"&gt;;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br&gt;&lt;/font&gt;&lt;font color="blue"&gt;OPEN &lt;/font&gt;&lt;font color="black"&gt;c&lt;/font&gt;&lt;font color="gray"&gt;;&lt;br&gt;&amp;nbsp;&amp;nbsp; &lt;br&gt;&lt;/font&gt;&lt;font color="blue"&gt;FETCH NEXT&lt;/font&gt;&lt;font color="black"&gt; &lt;/font&gt;&lt;font color="blue"&gt;FROM &lt;/font&gt;&lt;font color="black"&gt;c &lt;/font&gt;&lt;font color="blue"&gt;INTO &lt;/font&gt;&lt;font color="#434343"&gt;@n&lt;/font&gt;&lt;font color="gray"&gt;;&lt;br&gt;&amp;nbsp;&amp;nbsp; &lt;br&gt;&lt;/font&gt;&lt;font color="blue"&gt;WHILE &lt;/font&gt;&lt;font&gt;&lt;font color="magenta"&gt;@@FETCH_STATUS&lt;/font&gt;&lt;/font&gt;&lt;font color="#434343"&gt; =&lt;/font&gt;&lt;font color="blue"&gt; &lt;/font&gt;&lt;font color="black"&gt;0&lt;br&gt;&lt;/font&gt;&lt;font color="blue"&gt;BEGIN&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; SET &lt;/font&gt;&lt;font color="#434343"&gt;@s =&lt;/font&gt;&lt;font color="blue"&gt; &lt;/font&gt;&lt;font color="magenta"&gt;COALESCE&lt;/font&gt;&lt;font color="gray"&gt;(&lt;/font&gt;&lt;font color="#434343"&gt;@s &lt;/font&gt;&lt;font color="gray"&gt;+ &lt;/font&gt;&lt;font color="red"&gt;',' &lt;/font&gt;&lt;font color="gray"&gt;+ &lt;/font&gt;&lt;font color="#434343"&gt;@n&lt;/font&gt;&lt;font color="gray"&gt;, &lt;/font&gt;&lt;font color="#434343"&gt;@n&lt;/font&gt;&lt;font color="gray"&gt;);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;font color="blue"&gt;FETCH NEXT&lt;/font&gt;&lt;font color="black"&gt; &lt;/font&gt;&lt;font color="blue"&gt;FROM &lt;/font&gt;&lt;font color="black"&gt;c &lt;/font&gt;&lt;font color="blue"&gt;INTO &lt;/font&gt;&lt;font color="#434343"&gt;@n&lt;/font&gt;&lt;font color="gray"&gt;;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br&gt;&lt;/font&gt;&lt;font color="blue"&gt;END&lt;br&gt;&lt;br&gt;CLOSE &lt;/font&gt;&lt;font color="black"&gt;c&lt;/font&gt;&lt;font color="gray"&gt;;&lt;br&gt;&lt;/font&gt;&lt;font color="blue"&gt;DEALLOCATE &lt;/font&gt;&lt;font color="black"&gt;c&lt;/font&gt;&lt;font color="gray"&gt;;&lt;/font&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;/blockquote&gt;
&lt;b&gt;&lt;font size="4"&gt;&lt;br&gt;Approach #1b : An Optimized Cursor&lt;/font&gt;&lt;/b&gt;
&lt;p&gt;The following cursor yields better performance than a default cursor, even if a few of the specified cursor options may truthfully have no impact.&lt;br&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;table bgcolor="#eeeeee" cellpadding="0" cellspacing="0"&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;pre style="padding:10px 20px;font-size:12px;font-family:consolas,lucida console,courier new,courier;-moz-background-inline-policy:continuous;"&gt;&lt;font color="blue"&gt;DECLARE &lt;/font&gt;&lt;font color="black"&gt;c &lt;/font&gt;&lt;font color="blue"&gt;CURSOR&lt;br&gt;    LOCAL STATIC FORWARD_ONLY READ_ONLY&lt;br&gt;&lt;/font&gt;&lt;font color="black"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;font color="blue"&gt;FOR &lt;br&gt;    SELECT &lt;/font&gt;&lt;font color="black"&gt;name &lt;/font&gt;&lt;font color="blue"&gt;FROM &lt;/font&gt;&lt;font color="#434343"&gt;@foo &lt;/font&gt;&lt;font color="blue"&gt;ORDER BY &lt;/font&gt;&lt;font color="black"&gt;name&lt;/font&gt;&lt;font color="gray"&gt;;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br&gt;&lt;/font&gt;&lt;font color="blue"&gt;OPEN &lt;/font&gt;&lt;font color="black"&gt;c&lt;/font&gt;&lt;font color="gray"&gt;;&lt;br&gt;&amp;nbsp;&amp;nbsp; &lt;br&gt;&lt;/font&gt;&lt;font color="blue"&gt;FETCH NEXT&lt;/font&gt;&lt;font color="black"&gt; &lt;/font&gt;&lt;font color="blue"&gt;FROM &lt;/font&gt;&lt;font color="black"&gt;c &lt;/font&gt;&lt;font color="blue"&gt;INTO &lt;/font&gt;&lt;font color="#434343"&gt;@n&lt;/font&gt;&lt;font color="gray"&gt;;&lt;br&gt;&amp;nbsp;&amp;nbsp; &lt;br&gt;&lt;/font&gt;&lt;font color="blue"&gt;WHILE &lt;/font&gt;&lt;font&gt;&lt;font color="magenta"&gt;@@FETCH_STATUS&lt;/font&gt;&lt;/font&gt;&lt;font color="#434343"&gt; =&lt;/font&gt;&lt;font color="blue"&gt; &lt;/font&gt;&lt;font color="black"&gt;0&lt;br&gt;&lt;/font&gt;&lt;font color="blue"&gt;BEGIN&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; SET &lt;/font&gt;&lt;font color="#434343"&gt;@s =&lt;/font&gt;&lt;font color="blue"&gt; &lt;/font&gt;&lt;font color="magenta"&gt;COALESCE&lt;/font&gt;&lt;font color="gray"&gt;(&lt;/font&gt;&lt;font color="#434343"&gt;@s &lt;/font&gt;&lt;font color="gray"&gt;+ &lt;/font&gt;&lt;font color="red"&gt;',' &lt;/font&gt;&lt;font color="gray"&gt;+ &lt;/font&gt;&lt;font color="#434343"&gt;@n&lt;/font&gt;&lt;font color="gray"&gt;, &lt;/font&gt;&lt;font color="#434343"&gt;@n&lt;/font&gt;&lt;font color="gray"&gt;);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;font color="blue"&gt;FETCH NEXT&lt;/font&gt;&lt;font color="black"&gt; &lt;/font&gt;&lt;font color="blue"&gt;FROM &lt;/font&gt;&lt;font color="black"&gt;c &lt;/font&gt;&lt;font color="blue"&gt;INTO &lt;/font&gt;&lt;font color="#434343"&gt;@n&lt;/font&gt;&lt;font color="gray"&gt;;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br&gt;&lt;/font&gt;&lt;font color="blue"&gt;END&lt;br&gt;&lt;br&gt;CLOSE &lt;/font&gt;&lt;font color="black"&gt;c&lt;/font&gt;&lt;font color="gray"&gt;;&lt;br&gt;&lt;/font&gt;&lt;font color="blue"&gt;DEALLOCATE &lt;/font&gt;&lt;font color="black"&gt;c&lt;/font&gt;&lt;font color="gray"&gt;;&lt;/font&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;/blockquote&gt;

&lt;p&gt;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;I also see people suggesting that DEALLOCATE is sufficient, and that 
you don't need to CLOSE a cursor. For a long time, I would have agreed 
with them; however, it was pointed out to me last year (and I apologize,
 I tried to find the discussion, but failed), that you can actually 
observe some additional overhead if you fail to issue a CLOSE. I have 
plenty more to say about cursors, but I want to focus on the topic at 
hand, and will revisit these issues in a future blog post.&lt;br&gt;&lt;/p&gt;
 
&lt;p&gt;&lt;b&gt;&lt;font size="4"&gt;&lt;br&gt;Approach #2 : A While Loop&lt;/font&gt;&lt;/b&gt;
&lt;/p&gt;
&lt;b&gt;&lt;font size="4"&gt;&lt;/font&gt;&lt;/b&gt;
&lt;p&gt;A statement I hear a lot: "Don't use a cursor; use a while loop. They're faster." A loop is a loop; an iterative approach has the same performance limitations whether or not you explicitly use DECLARE CURSOR / FETCH. The while loop is a little tidier than the cursor, but relies on the IDENTITY column to iterate:&lt;/p&gt;

&lt;blockquote&gt;
&lt;table bgcolor="#eeeeee" cellpadding="0" cellspacing="0"&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;pre style="padding:10px 20px;font-size:12px;font-family:consolas,lucida console,courier new,courier;-moz-background-inline-policy:continuous;"&gt;&lt;font color="blue"&gt;WHILE &lt;/font&gt;&lt;font color="#434343"&gt;@loop &lt;/font&gt;&lt;font color="gray"&gt;&amp;lt; &lt;/font&gt;&lt;font color="#434343"&gt;@count&lt;br&gt;&lt;/font&gt;&lt;font color="blue"&gt;BEGIN&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; SELECT TOP &lt;/font&gt;&lt;font color="black"&gt;1 &lt;/font&gt;&lt;font color="#434343"&gt;@s =&lt;/font&gt;&lt;font color="blue"&gt; &lt;/font&gt;&lt;font color="magenta"&gt;COALESCE&lt;/font&gt;&lt;font color="gray"&gt;(&lt;/font&gt;&lt;font color="#434343"&gt;@s &lt;/font&gt;&lt;font color="gray"&gt;+ &lt;/font&gt;&lt;font color="red"&gt;',' &lt;/font&gt;&lt;font color="gray"&gt;+ &lt;/font&gt;&lt;font color="black"&gt;name&lt;/font&gt;&lt;font color="gray"&gt;, &lt;/font&gt;&lt;font color="black"&gt;name&lt;/font&gt;&lt;font color="gray"&gt;)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;font color="blue"&gt;FROM &lt;/font&gt;&lt;font color="#434343"&gt;@foo&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;font color="blue"&gt;WHERE &lt;/font&gt;&lt;font color="black"&gt;id &lt;/font&gt;&lt;font color="gray"&gt;&amp;gt; &lt;/font&gt;&lt;font color="#434343"&gt;@loop&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;font color="blue"&gt;ORDER BY &lt;/font&gt;&lt;font color="black"&gt;id&lt;/font&gt;&lt;font color="gray"&gt;;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;font color="blue"&gt;SET &lt;/font&gt;&lt;font color="#434343"&gt;@loop &lt;/font&gt;&lt;font color="gray"&gt;+=&lt;/font&gt;&lt;font color="blue"&gt; &lt;/font&gt;&lt;font color="black"&gt;1&lt;/font&gt;&lt;font color="gray"&gt;;&lt;br&gt;&lt;/font&gt;&lt;font color="blue"&gt;END &lt;/font&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;br&gt;&lt;font size="4"&gt;&lt;b&gt;Approach #3 : An Undocumented / Unsupported Aggregation&lt;/b&gt;&lt;/font&gt;&lt;/p&gt;

&lt;p&gt;This is probably the approach I've used the most in my career - it's essentially a cursor disguised as a set-based operation. I believe I first learned this approach from Anith Sen over a decade ago.&lt;br&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;table bgcolor="#eeeeee" cellpadding="0" cellspacing="0"&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;pre style="padding:10px 20px;font-size:12px;font-family:consolas,lucida console,courier new,courier;-moz-background-inline-policy:continuous;"&gt;&lt;font color="blue"&gt;SELECT &lt;/font&gt;&lt;font color="#434343"&gt;@s =&lt;/font&gt;&lt;font color="blue"&gt; &lt;/font&gt;&lt;font color="magenta"&gt;COALESCE&lt;/font&gt;&lt;font color="gray"&gt;(&lt;/font&gt;&lt;font color="#434343"&gt;@s &lt;/font&gt;&lt;font color="gray"&gt;+ &lt;/font&gt;&lt;font color="red"&gt;',' &lt;/font&gt;&lt;font color="gray"&gt;+ &lt;/font&gt;&lt;font color="black"&gt;name&lt;/font&gt;&lt;font color="gray"&gt;, &lt;/font&gt;&lt;font color="black"&gt;name&lt;/font&gt;&lt;font color="gray"&gt;)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="blue"&gt;FROM &lt;/font&gt;&lt;font color="#434343"&gt;@foo&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="blue"&gt;ORDER BY &lt;/font&gt;&lt;font color="black"&gt;name&lt;/font&gt;&lt;font color="gray"&gt;;&lt;/font&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;br&gt;&lt;font size="4"&gt;&lt;b&gt;Approach #4a : FOR XML PATH&lt;/b&gt;&lt;/font&gt;&lt;/p&gt;

&lt;p&gt;When SQL Server 2005 was released, we were introduced to a much more powerful set of functionality around XML. One feature was the ability to convert a resultset into an XML string, and this was quickly exploited to join strings together without any XML tags at all, as follows: &lt;br&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;table bgcolor="#eeeeee" cellpadding="0" cellspacing="0"&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;pre style="padding:10px 20px;font-size:12px;font-family:consolas,lucida console,courier new,courier;-moz-background-inline-policy:continuous;"&gt;&lt;font color="gray"&gt;;&lt;/font&gt;&lt;font color="blue"&gt;WITH &lt;/font&gt;&lt;font color="black"&gt;x&lt;/font&gt;&lt;font color="gray"&gt;(&lt;/font&gt;&lt;font color="black"&gt;x&lt;/font&gt;&lt;font color="gray"&gt;) &lt;/font&gt;&lt;font color="blue"&gt;AS&lt;br&gt;&lt;/font&gt;&lt;font color="gray"&gt;(&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;font color="blue"&gt;SELECT &lt;/font&gt;&lt;font color="black"&gt;name &lt;/font&gt;&lt;font color="blue"&gt;AS &lt;/font&gt;&lt;font color="black"&gt;[data()]&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;font color="blue"&gt;FROM &lt;/font&gt;&lt;font color="#434343"&gt;@foo&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;font color="blue"&gt;ORDER BY &lt;/font&gt;&lt;font color="black"&gt;name&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;font color="blue"&gt;FOR XML PATH&lt;/font&gt;&lt;font color="gray"&gt;(&lt;/font&gt;&lt;font color="red"&gt;''&lt;/font&gt;&lt;font color="gray"&gt;)&lt;br&gt;)&lt;br&gt;&lt;/font&gt;&lt;font color="blue"&gt;SELECT &lt;/font&gt;&lt;font color="#434343"&gt;@s =&lt;/font&gt;&lt;font color="blue"&gt; &lt;/font&gt;&lt;font color="magenta"&gt;REPLACE&lt;/font&gt;&lt;font color="gray"&gt;(&lt;/font&gt;&lt;font color="black"&gt;x&lt;/font&gt;&lt;font color="gray"&gt;, &lt;/font&gt;&lt;font color="red"&gt;' '&lt;/font&gt;&lt;font color="gray"&gt;, &lt;/font&gt;&lt;font color="red"&gt;','&lt;/font&gt;&lt;font color="gray"&gt;)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;font color="blue"&gt;FROM &lt;/font&gt;&lt;font color="black"&gt;x&lt;/font&gt;&lt;font color="gray"&gt;;&lt;/font&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;/blockquote&gt;


&lt;p&gt;&lt;br&gt;&lt;font size="4"&gt;&lt;b&gt;Approach #4b : FOR XML PATH, TYPE&lt;/b&gt;&lt;/font&gt;&lt;/p&gt;

&lt;p&gt;I picked up this approach from a comment by RBarryYoung on Adam Machanic's (&lt;a href="http://sqlblog.com/blogs/adam_machanic/" title="http://sqlblog.com/blogs/adam_machanic/" target="_blank"&gt;blog&lt;/a&gt; | &lt;a href="http://twitter.com/AdamMachanic" title="http://twitter.com/AdamMachanic" target="_blank"&gt;twitter&lt;/a&gt;) post from 2006 ("&lt;a href="http://sqlblog.com/blogs/adam_machanic/archive/2006/07/12/rowset-string-concatenation-which-method-is-best.aspx" title="http://sqlblog.com/blogs/adam_machanic/archive/2006/07/12/rowset-string-concatenation-which-method-is-best.aspx" target="_blank"&gt;Rowset string concatenation: Which method is best?&lt;/a&gt;"). It is similar to the above but uses value() and TYPE for extracting the string values from the XML. I just couldn't figure out how to pull the values out with the comma separator without having to hack off the last comma.&lt;br&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;table bgcolor="#eeeeee" cellpadding="0" cellspacing="0"&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;pre style="padding:10px 20px;font-size:12px;font-family:consolas,lucida console,courier new,courier;-moz-background-inline-policy:continuous;"&gt;&lt;font color="blue"&gt;SELECT &lt;/font&gt;&lt;font color="#434343"&gt;@s =&lt;/font&gt;&lt;font color="blue"&gt;&lt;br&gt;&lt;/font&gt;&lt;font color="gray"&gt;(&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;font color="blue"&gt;SELECT &lt;/font&gt;&lt;font color="black"&gt;name &lt;/font&gt;&lt;font color="gray"&gt;+ &lt;/font&gt;&lt;font color="red"&gt;','&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="blue"&gt;FROM &lt;/font&gt;&lt;font color="#434343"&gt;@foo&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="blue"&gt;ORDER BY &lt;/font&gt;&lt;font color="black"&gt;name&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="blue"&gt;FOR XML PATH&lt;/font&gt;&lt;font color="gray"&gt;(&lt;/font&gt;&lt;font color="red"&gt;''&lt;/font&gt;&lt;font color="gray"&gt;), &lt;/font&gt;&lt;font color="blue"&gt;TYPE&lt;br&gt;&lt;/font&gt;&lt;font color="gray"&gt;)&lt;/font&gt;&lt;font color="black"&gt;.value&lt;/font&gt;&lt;font color="gray"&gt;(&lt;/font&gt;&lt;font color="red"&gt;'.[1]'&lt;/font&gt;&lt;font color="gray"&gt;, &lt;/font&gt;&lt;font color="red"&gt;'NVARCHAR(MAX)'&lt;/font&gt;&lt;font color="gray"&gt;);&lt;br&gt;&lt;br&gt;&lt;/font&gt;&lt;font color="blue"&gt;SELECT &lt;/font&gt;&lt;font color="#434343"&gt;@s =&lt;/font&gt;&lt;font color="blue"&gt; &lt;/font&gt;&lt;font color="magenta"&gt;LEFT&lt;/font&gt;&lt;font color="gray"&gt;(&lt;/font&gt;&lt;font color="#434343"&gt;@s&lt;/font&gt;&lt;font color="gray"&gt;, &lt;/font&gt;&lt;font color="magenta"&gt;LEN&lt;/font&gt;&lt;font color="gray"&gt;(&lt;/font&gt;&lt;font color="#434343"&gt;@s&lt;/font&gt;&lt;font color="gray"&gt;)-&lt;/font&gt;&lt;font color="black"&gt;1&lt;/font&gt;&lt;font color="gray"&gt;);&lt;/font&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;/blockquote&gt;


&lt;p&gt;&lt;br&gt;&lt;font size="4"&gt;&lt;b&gt;Performance Test &lt;/b&gt;&lt;/font&gt;&lt;br&gt;&lt;/p&gt;

&lt;p&gt;I ran each of these code segments 1,000 times (script attached below). I also noted in Hugo's research that the situation can be even worse for the WHILE loop when the IDENTITY column is not the clustering key, so I tried the script again with the PRIMARY KEY on the name column of the table variable instead of the id column. Below are all of the results: &lt;/p&gt;

&lt;blockquote&gt;
&lt;table style="border-collapse:collapse;" cellpadding="5" cellspacing="0"&gt;
&lt;tr&gt;
&lt;th style="border:0px;"&gt;&amp;nbsp;&lt;/th&gt;
&lt;th style="border:1px solid black;" align="center"&gt;IDENTITY = PK&lt;/th&gt;
&lt;th style="border:0px;"&gt;&amp;nbsp;&lt;/th&gt;
&lt;th style="border:1px solid black;" align="center"&gt;name = PK&lt;/th&gt;
&lt;th style="border:0px;"&gt;&amp;nbsp;&lt;/th&gt;
&lt;/tr&gt;


&lt;tr&gt;
&lt;th style="border:0px;"&gt;&amp;nbsp;&lt;/th&gt;
&lt;th style="border:1px solid black;" align="center"&gt;Total Duration&lt;br&gt;&lt;span style="font-weight:normal;"&gt;(milliseconds)&lt;/span&gt;&lt;/th&gt;
&lt;th style="border:1px solid black;" align="center"&gt;Average Duration&lt;br&gt;&lt;span style="font-weight:normal;"&gt;(milliseconds)&lt;/span&gt;&lt;/th&gt;
&lt;th style="border:1px solid black;" align="center"&gt;Total Duration&lt;br&gt;&lt;span style="font-weight:normal;"&gt;(milliseconds)&lt;/span&gt;&lt;/th&gt;
&lt;th style="border:1px solid black;" align="center"&gt;Average Duration&lt;br&gt;&lt;span style="font-weight:normal;"&gt;(milliseconds)&lt;/span&gt;&lt;/th&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td style="border:1px solid black;"&gt;1a : Default CURSOR&lt;/td&gt;

&lt;td style="border:1px solid black;" align="right"&gt;105,060&lt;/td&gt;

&lt;td style="border:1px solid black;" align="right"&gt;105.1&lt;/td&gt;

&lt;td style="border:1px solid black;" align="right"&gt;104,843&lt;/td&gt;

&lt;td style="border:1px solid black;" align="right"&gt;104.8&lt;/td&gt;

&lt;/tr&gt;

&lt;tr&gt;
&lt;td style="border:1px solid black;"&gt;1b : Optimized CURSOR&lt;/td&gt;

&lt;td style="border:1px solid black;" align="right"&gt;84,710&lt;/td&gt;

&lt;td style="border:1px solid black;" align="right"&gt;84.7&lt;/td&gt;

&lt;td style="border:1px solid black;" align="right"&gt;85,013&lt;br&gt;&lt;/td&gt;

&lt;td style="border:1px solid black;" align="right"&gt;85.0&lt;/td&gt;
&lt;/tr&gt;


&lt;tr&gt;
&lt;td style="border:1px solid black;"&gt;2&amp;nbsp;&amp;nbsp; : WHILE Loop&lt;/td&gt;

&lt;td style="border:1px solid black;" align="right"&gt;&lt;font color="#cc0000"&gt;&lt;b&gt;113,753&lt;/b&gt;&lt;/font&gt;&lt;/td&gt;

&lt;td style="border:1px solid black;" align="right"&gt;&lt;font color="#cc0000"&gt;&lt;b&gt;113.8&lt;/b&gt;&lt;/font&gt;&lt;/td&gt;

&lt;td style="border:1px solid black;" align="right"&gt;&lt;font color="#cc0000"&gt;&lt;b&gt;516,136&lt;/b&gt;&lt;/font&gt;&lt;/td&gt;

&lt;td style="border:1px solid black;" align="right"&gt;&lt;font color="#cc0000"&gt;&lt;b&gt;516.1&lt;/b&gt;&lt;/font&gt;&lt;/td&gt;
&lt;/tr&gt;


&lt;tr&gt;
&lt;td style="border:1px solid black;"&gt;3&amp;nbsp;&amp;nbsp; : Set-Based "Cursor"&lt;/td&gt;

&lt;td style="border:1px solid black;" align="right"&gt;47,640&lt;/td&gt;

&lt;td style="border:1px solid black;" align="right"&gt;47.6&lt;/td&gt;

&lt;td style="border:1px solid black;" align="right"&gt;46,750&lt;/td&gt;

&lt;td style="border:1px solid black;" align="right"&gt;46.8&lt;/td&gt;
&lt;/tr&gt;


&lt;tr&gt;
&lt;td style="border:1px solid black;"&gt;4a : FOR XML PATH&lt;/td&gt;

&lt;td style="border:1px solid black;" align="right"&gt;7,113&lt;/td&gt;

&lt;td style="border:1px solid black;" align="right"&gt;7.1&lt;/td&gt;

&lt;td style="border:1px solid black;" align="right"&gt;6,753&lt;/td&gt;

&lt;td style="border:1px solid black;" align="right"&gt;6.8&lt;/td&gt;
&lt;/tr&gt;


&lt;tr&gt;
&lt;td style="border:1px solid black;"&gt;4b : FOR XML PATH, TYPE&lt;/td&gt;

&lt;td style="border:1px solid black;" align="right"&gt;&lt;font color="#009900"&gt;&lt;b&gt;2,486&lt;/b&gt;&lt;/font&gt;&lt;/td&gt;

&lt;td style="border:1px solid black;" align="right"&gt;&lt;font color="#009900"&gt;&lt;b&gt;2.5&lt;/b&gt;&lt;/font&gt;&lt;/td&gt;

&lt;td style="border:1px solid black;" align="right"&gt;&lt;font color="#009900"&gt;&lt;b&gt;1,813&lt;/b&gt;&lt;/font&gt;&lt;/td&gt;

&lt;td style="border:1px solid black;" align="right"&gt;&lt;font color="#009900"&gt;&lt;b&gt;1.8&lt;/b&gt;&lt;/font&gt;&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;br&gt;&lt;font size="4"&gt;&lt;b&gt;Conclusion&lt;/b&gt;&lt;/font&gt; &lt;br&gt;&lt;/p&gt;

&lt;p&gt;Clearly, at least in this specific scenario, the XML methods are far superior to any iterative approach.&amp;nbsp; The disadvantage of the XML methods is that, like MERGE and other new syntax constructs, they are difficult to memorize and get right without referencing existing, working code. But if your goal is performance, the up-front and maintenance cost is going to be worth it in the long run. If you are working on a one-off, then the set-based concatenation (Approach #3) is likely a good trade-off. But in very single test, the WHILE loop is the worst performer. Perhaps there is some flawed logic in how I am constructing my loop; there are many ways to do it, this one just seemed the most intuitive and, more importantly, most efficient - at least among the typical WHILE loop methods.&lt;br&gt;&lt;/p&gt;
I hope these results are something you keep in mind the next time you need to perform string concatenation in T-SQL, and you don't have the luxury of doing it elsewhere (e.g. in the application tier or in SQLCLR).

&lt;p&gt;&amp;nbsp; &lt;br&gt;&lt;/p&gt;</description></item></channel></rss>