<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://sqlservernation.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>Development : Intermediate SQL</title><link>http://sqlservernation.com/blogs/development/archive/tags/Intermediate+SQL/default.aspx</link><description>Tags: Intermediate SQL</description><dc:language>en</dc:language><generator>CommunityServer 2008.5 SP2 (Build: 40407.4157)</generator><item><title>Unstringing a comma delimited column</title><link>http://sqlservernation.com/blogs/development/archive/2009/05/28/unstringing-a-comma-delimited-column.aspx</link><pubDate>Wed, 27 May 2009 18:07:00 GMT</pubDate><guid isPermaLink="false">a3dbd76b-19b9-441f-b40a-d79e8f433d35:205</guid><dc:creator>mark_wills</dc:creator><slash:comments>1</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://sqlservernation.com/blogs/development/rsscomments.aspx?PostID=205</wfw:commentRss><comments>http://sqlservernation.com/blogs/development/archive/2009/05/28/unstringing-a-comma-delimited-column.aspx#comments</comments><description>&lt;p&gt;Ever had a column which had a pile of information&amp;nbsp;delimited by commas&amp;nbsp;that you want to unpack ?&lt;/p&gt;
&lt;p&gt;Well, it is not that hard so long as the information is pretty short, and tightly controlled... &lt;/p&gt;
&lt;p&gt;
&lt;p&gt;&lt;span style="font-size:x-small;"&gt;&lt;/span&gt;&lt;/p&gt;
&lt;span style="font-size:x-small;"&gt;&lt;/span&gt;
&lt;p&gt;
&lt;p&gt;
&lt;p&gt;&amp;nbsp;-- step 1 create a sample table used as a datasource...&lt;/p&gt;
&lt;p&gt;if object_id(&amp;#39;tempdb..#tmp_colors&amp;#39;,&amp;#39;U&amp;#39;) is not null drop table #tmp_colors&lt;br /&gt;&amp;nbsp;&lt;br /&gt;create table #tmp_colors (Date datetime, Name varchar(60), Choices varchar(100))&lt;br /&gt;insert #tmp_colors&lt;br /&gt;select &amp;#39;01 Mar 2009&amp;#39;,&amp;#39;John S&amp;#39;,&amp;#39;Red,Blue,Yellow&amp;#39; union all&lt;br /&gt;select &amp;#39;02 Mar 2009&amp;#39;,&amp;#39;Jane D&amp;#39;,&amp;#39;Green, Orange, Pink, Purple&amp;#39; union all&lt;br /&gt;select &amp;#39;01 Mar 2009&amp;#39;,&amp;#39;Mark W&amp;#39;,&amp;#39;Yellow, Blue, Black, Dark Blue&amp;#39; union all&lt;br /&gt;select &amp;#39;02 Mar 2009&amp;#39;,&amp;#39;Brandon G&amp;#39;,&amp;#39;Dark Blue, Navy, Yellow, Gray, Purple&amp;#39; union all&lt;br /&gt;select &amp;#39;01 Mar 2009&amp;#39;,&amp;#39;Tim C&amp;#39;,&amp;#39;Blue, Cyan, Yellow, Gray&amp;#39; union all&lt;br /&gt;select &amp;#39;02 Mar 2009&amp;#39;,&amp;#39;Bob J&amp;#39;,&amp;#39;Blue&amp;#39;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;-- now lets run a query on that and return the results.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;SELECT name, date, ltrim(SUBSTRING(choices, n, CHARINDEX(&amp;#39;,&amp;#39;, choices + &amp;#39;,&amp;#39;,n) - n)) AS colours&lt;br /&gt;FROM #tmp_colors&lt;br /&gt;CROSS JOIN (SELECT number n FROM master..spt_values WHERE type = &amp;#39;P&amp;#39;) AS Numbers&lt;br /&gt;WHERE SUBSTRING(&amp;#39;,&amp;#39; + choices, n, 1) = &amp;#39;,&amp;#39;&lt;br /&gt;AND n&amp;nbsp;&amp;lt; LEN(choices) + 1&lt;/p&gt;
&lt;/p&gt;
&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;&lt;span style="font-size:9pt;color:black;font-family:Arial;"&gt;&lt;/span&gt;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://sqlservernation.com/aggbug.aspx?PostID=205" width="1" height="1"&gt;</description><category domain="http://sqlservernation.com/blogs/development/archive/tags/Intermediate+SQL/default.aspx">Intermediate SQL</category><category domain="http://sqlservernation.com/blogs/development/archive/tags/Mark+Wills/default.aspx">Mark Wills</category></item><item><title>Two things I love about Management Studio 2008</title><link>http://sqlservernation.com/blogs/development/archive/2009/04/03/two-things-i-love-about-management-studio-2008.aspx</link><pubDate>Sat, 04 Apr 2009 01:34:00 GMT</pubDate><guid isPermaLink="false">a3dbd76b-19b9-441f-b40a-d79e8f433d35:175</guid><dc:creator>TimChapman</dc:creator><slash:comments>0</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://sqlservernation.com/blogs/development/rsscomments.aspx?PostID=175</wfw:commentRss><comments>http://sqlservernation.com/blogs/development/archive/2009/04/03/two-things-i-love-about-management-studio-2008.aspx#comments</comments><description>&lt;p&gt;I&amp;#39;ve been using the SQL Server product for quite some time now, and I almost always use the IDE programs that ship with the database engine.&amp;nbsp; Enterprise Manager and Query Analyzer certainly had their quicks, but were overall nice tools.&amp;nbsp; Management Studio 2005 was a great improvement over these tools because it integrated Enterprise Manager and Query Analyzer into one tool, and it added some neat bells and whistles.&amp;nbsp; Now that Management Studio 2008 is out, there are two really neat new features I&amp;#39;d like you to take a look at if you haven&amp;#39;t already.&amp;nbsp; &lt;/p&gt;
&lt;p&gt;First, is the ability to expand and collapse code blocks.&amp;nbsp; This is a subtle tool, but so useful when you really just want to focus on a single set of code.&amp;nbsp; Its not a huge feature for the product, but one that I think makes all of the difference in the world.&amp;nbsp; In the following screenshot, I define a table and then use a BEGIN...END statement to insert some rows into the table...simple stuff.&amp;nbsp; &lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;a href="http://sqlservernation.com/cfs-file.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/development.TwoThingsILoveAboutSSMS2008/TwoThingsILoveAboutSSMS2008_5F00_1_5F00_Brackets.JPG"&gt;&lt;img border="0" src="http://sqlservernation.com/resized-image.ashx/__size/550x0/__key/CommunityServer.Blogs.Components.WeblogFiles/development.TwoThingsILoveAboutSSMS2008/TwoThingsILoveAboutSSMS2008_5F00_1_5F00_Brackets.JPG" alt="" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Here&amp;#39;s the code that I use in the above screenshot.&lt;/p&gt;
&lt;pre style="font-size:12px;overflow:auto;width:100%;color:#000000;line-height:14px;font-family:Andale Mono, Lucida Console, Monaco, fixed, monospace;background-color:#eee;border:#999999 1px dashed;padding:5px;"&gt;&lt;code&gt;IF OBJECT_ID(&amp;#39;RecordLoad&amp;#39;) IS NOT NULL
DROP TABLE RecordLoad
GO
CREATE TABLE RecordLoad
(
    ID INT IDENTITY(1,1),
    ValueColumn INT
)
GO

BEGIN
    ;WITH CounterCTE
    AS
    (
        SELECT 1 AS ValRow
        UNION ALL
        SELECT ValRow + 1
        FROM CounterCTE
        WHERE ValRow &amp;lt; 100
    )
        INSERT INTO RecordLoad(ValueColumn)
        SELECT ValRow 
        FROM CounterCTE
END
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;Notice the expanded brackets to the left of the code blocks.&amp;nbsp; If I am done writing that block, or I just want to come back to it later, I can click on the box and collapse the code block so that it takes up only one line in the IDE.&amp;nbsp; I can even highlight this line and run the entire block of code contained in it.&amp;nbsp; This is very handy for when you want to run large snippets of data.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;The second thing I really like about Management Studio 2008 is its built in IntelliSense.&amp;nbsp; Sure, there are other tools out there from third parties that provide IntelliSense, but I don&amp;#39;t think they&amp;#39;re as good as what comes packaged with Management Studio 2008.&amp;nbsp; In the following example, I modify my INSERT statement to show the available columns to use in the INSERT list.&amp;nbsp; It provides a list of the fields along with the data type of the highlighted column.&amp;nbsp; This is very handy information, and can make you a lot more productive.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;a href="http://sqlservernation.com/cfs-file.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/development.TwoThingsILoveAboutSSMS2008/TwoThingsILoveAboutSSMS2008_5F00_3_5F00_Sense.JPG"&gt;&lt;img border="0" src="http://sqlservernation.com/resized-image.ashx/__size/550x0/__key/CommunityServer.Blogs.Components.WeblogFiles/development.TwoThingsILoveAboutSSMS2008/TwoThingsILoveAboutSSMS2008_5F00_3_5F00_Sense.JPG" alt="" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;Both of these features are based are technically part of IntelliSense, which can be enabled by right-clicking in the Query window and selecting &amp;quot;IntelliSense Enabled&amp;quot;.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;While I certainly don&amp;#39;t think these are the best features that came with SQL Server 2008, or even the best enhancements to Management Studio 2008, I love them.&amp;nbsp; I think they made code easier to write, and debug.&amp;nbsp; Check them out if you haven&amp;#39;t already.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://sqlservernation.com/aggbug.aspx?PostID=175" width="1" height="1"&gt;</description><category domain="http://sqlservernation.com/blogs/development/archive/tags/Intermediate+SQL/default.aspx">Intermediate SQL</category><category domain="http://sqlservernation.com/blogs/development/archive/tags/Beginner+SQL/default.aspx">Beginner SQL</category><category domain="http://sqlservernation.com/blogs/development/archive/tags/Advanced+SQL/default.aspx">Advanced SQL</category></item><item><title>Using TRY...CATCH IN SQL Server 2005/2008</title><link>http://sqlservernation.com/blogs/development/archive/2009/04/03/using-try-catch-in-sql-server-2005-2008.aspx</link><pubDate>Fri, 03 Apr 2009 19:16:00 GMT</pubDate><guid isPermaLink="false">a3dbd76b-19b9-441f-b40a-d79e8f433d35:159</guid><dc:creator>TimChapman</dc:creator><slash:comments>0</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://sqlservernation.com/blogs/development/rsscomments.aspx?PostID=159</wfw:commentRss><comments>http://sqlservernation.com/blogs/development/archive/2009/04/03/using-try-catch-in-sql-server-2005-2008.aspx#comments</comments><description>&lt;p&gt;&amp;nbsp;New to SQL Server 2005 (and present in SQL Server 2008) is the ability to use TRY..CATCH constructs inside of your TSQL code to catch error statements.&amp;nbsp; If you&amp;#39;ve done any type of programming in other programming languages, you know that this is a very handy tool to have.&amp;nbsp; Whats even better in my opinion is the ability to write a generic error handling procedure you can use inside of your CATCH blocks to capture and record errors when they occur.&amp;nbsp; &lt;/p&gt;
&lt;p&gt;So, lets take a look at how we can take advantage of these new error handling features.&amp;nbsp; In the following script I create a table which will be used by my stored procedure which will contain my TRY...CATCH blocks.&lt;/p&gt;
&lt;pre style="font-size:12px;overflow:auto;width:100%;color:#000000;line-height:14px;font-family:Andale Mono, Lucida Console, Monaco, fixed, monospace;background-color:#eee;border:#999999 1px dashed;padding:5px;"&gt;&lt;code&gt;IF OBJECT_ID(&amp;#39;RecordLoad&amp;#39;) IS NOT NULL
DROP TABLE RecordLoad
GO
CREATE TABLE RecordLoad
(
    ID INT IDENTITY(1,1),
    ValueColumn INT
)
GO
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;Here I define a stored procedure that uses a CTE to insert 100 records into the table I just created.&amp;nbsp; This is done inside of the TRY block, which will immediately revert any execution to the CATCH block once an error is incurred for which it is able to handle.&amp;nbsp; After the insert of the 100 records, I cause a divide by zero error.&amp;nbsp; This error is caught by my CATCH block, which then displays information to me regarding the error details.&amp;nbsp; To get at this error related information, I use 6 new error handling functions.&lt;/p&gt;
&lt;pre style="font-size:12px;overflow:auto;width:100%;color:#000000;line-height:14px;font-family:Andale Mono, Lucida Console, Monaco, fixed, monospace;background-color:#eee;border:#999999 1px dashed;padding:5px;"&gt;&lt;code&gt;IF OBJECT_ID(&amp;#39;usp_ErrorExample&amp;#39;) IS NOT NULL
DROP PROCEDURE usp_ErrorExample
GO
CREATE PROCEDURE usp_ErrorExample
AS
BEGIN
    BEGIN TRY
        
        ;WITH CounterCTE
        AS
        (
            SELECT 1 AS ValRow
            UNION ALL
            SELECT ValRow + 1
            FROM CounterCTE
            WHERE ValRow &amp;lt; 100
        )
            INSERT INTO RecordLoad(ValueColumn)
            SELECT ValRow 
            FROM CounterCTE
            
        SELECT Error = 1/0  --THIS WILL ERROR, AND INVOKE THE CATCH BLOCK
    END TRY
    BEGIN CATCH
        --USE NEW ERROR HANDLING FUNCTIONS TO GET ERROR DATA
        SELECT 
            ErrorLine = ERROR_LINE(), 
            ErrorMessage = ERROR_MESSAGE(), 
            ErrorNumber = ERROR_NUMBER(),
            ErrorProcedure = ERROR_PROCEDURE(),
            ErrorSeverity = ERROR_SEVERITY(),
            ErrorState = ERROR_STATE()
    END CATCH
END
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;Here&amp;#39;s a screenshot of what my output from the above procedure execution looks like.&amp;nbsp; As you can see, this is some very handy information to have.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://sqlservernation.com/cfs-file.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/development.ErrorHandlingTryCatch/1_5F00_ErrorHandling_5F00_InitialOutput.JPG"&gt;&lt;img border="0" src="http://sqlservernation.com/resized-image.ashx/__size/550x0/__key/CommunityServer.Blogs.Components.WeblogFiles/development.ErrorHandlingTryCatch/1_5F00_ErrorHandling_5F00_InitialOutput.JPG" alt="" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The following procedure is almost the same as above, except that I am using the RAISERROR command to generate a custom error message, which is also caught by the CATCH block.&amp;nbsp; &lt;/p&gt;
&lt;pre style="font-size:12px;overflow:auto;width:100%;color:#000000;line-height:14px;font-family:Andale Mono, Lucida Console, Monaco, fixed, monospace;background-color:#eee;border:#999999 1px dashed;padding:5px;"&gt;&lt;code&gt;IF OBJECT_ID(&amp;#39;usp_ErrorExample&amp;#39;) IS NOT NULL
DROP PROCEDURE usp_ErrorExample
GO
CREATE PROCEDURE usp_ErrorExample
AS
BEGIN
    BEGIN TRY
        
        ;WITH CounterCTE
        AS
        (
            SELECT 1 AS ValRow
            UNION ALL
            SELECT ValRow + 1
            FROM CounterCTE
            WHERE ValRow &amp;lt; 100
        )
            INSERT INTO RecordLoad(ValueColumn)
            SELECT ValRow 
            FROM CounterCTE
            
        RAISERROR(&amp;#39;This is a custom error.&amp;#39;, 16,1,1)  --THIS WILL ERROR, AND INVOKE THE CATCH BLOCK
    END TRY
    BEGIN CATCH
        --USE NEW ERROR HANDLING FUNCTIONS TO GET ERROR DATA
        SELECT 
            ErrorLine = ERROR_LINE(), 
            ErrorMessage = ERROR_MESSAGE(), 
            ErrorNumber = ERROR_NUMBER(),
            ErrorProcedure = ERROR_PROCEDURE(),
            ErrorSeverity = ERROR_SEVERITY(),
            ErrorState = ERROR_STATE()
    END CATCH
END
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And here is the output from that procedure call.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;a href="http://sqlservernation.com/cfs-file.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/development.ErrorHandlingTryCatch/2_5F00_ErrorHandling_5F00_Custom.JPG"&gt;&lt;img border="0" src="http://sqlservernation.com/resized-image.ashx/__size/550x0/__key/CommunityServer.Blogs.Components.WeblogFiles/development.ErrorHandlingTryCatch/2_5F00_ErrorHandling_5F00_Custom.JPG" alt="" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Finding out about these errors are great when they occur, but having to duplicate the code to display to the screen or insert to a table can be a cumbersome and potentially error prone task.&amp;nbsp; Whats great about using the TRY...CATCH blocks is that I can define a generic stored procedure to save these messages to a table for later review.&amp;nbsp; The following code snippets I have borrowed &lt;a href="http://articles.techrepublic.com.com/5100-10878_11-6084469.html" title="Detailed Error Handling Information"&gt;from an article I had previous written&lt;/a&gt; for TechRepublic.&amp;nbsp; I define a table to store the errors when they occur, and a procedure that I can call in all of my CATCH statements to log the error information.&amp;nbsp; The great thing is that since the procedure executes in the context of the CATCH block, there is no need to pass in any parameters, the functions take care of the details for me.&lt;/p&gt;
&lt;pre style="font-size:12px;overflow:auto;width:100%;color:#000000;line-height:14px;font-family:Andale Mono, Lucida Console, Monaco, fixed, monospace;background-color:#eee;border:#999999 1px dashed;padding:5px;"&gt;&lt;code&gt;CREATE TABLE ProcedureErrors
(      
    ErrorLogID INT IDENTITY(1,1) PRIMARY KEY,      
    ErrorDate DATETIME DEFAULT(GETDATE()),      
    ErrorLine INT,      
    ErrorMessage NVARCHAR(4000),      
    ErrorNumber INT,      
    ErrorProcedure NVARCHAR(126),      
    ErrorSeverity INT,      
    ErrorState INT,       
    DatabaseName NVARCHAR(255)
)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre style="font-size:12px;overflow:auto;width:100%;color:#000000;line-height:14px;font-family:Andale Mono,Lucida Console,Monaco,fixed,monospace;background-color:#eeeeee;border:#999999 1px dashed;padding:5px;"&gt;&lt;code&gt;CREATE PROCEDURE [usp_LogProcedureError]
AS
BEGIN
    IF ERROR_NUMBER() &amp;lt;&amp;gt; 0 AND @@TRANCOUNT &amp;gt; 0
        ROLLBACK TRAN

    INSERT INTO ProcedureErrors
    (
        ErrorLine, ErrorMessage, ErrorNumber, 
        ErrorProcedure, ErrorSeverity, ErrorState, DatabaseName
    )
    VALUES
    (
        ERROR_LINE(), ERROR_MESSAGE(), ERROR_NUMBER(),
        ERROR_PROCEDURE(), ERROR_SEVERITY(), ERROR_STATE(), DB_NAME()
    )

    PRINT &amp;#39;An Error has occurred in &amp;#39; + ERROR_PROCEDURE() + &amp;#39;.&amp;#39;
    PRINT &amp;#39;Error Description: &amp;#39; + ERROR_MESSAGE()

END
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;And here is the altered procedure script with the new call to my usp_LogProcedureError procedure.&lt;/p&gt;
&lt;pre style="font-size:12px;overflow:auto;width:100%;color:#000000;line-height:14px;font-family:Andale Mono, Lucida Console, Monaco, fixed, monospace;background-color:#eee;border:#999999 1px dashed;padding:5px;"&gt;&lt;code&gt;IF OBJECT_ID(&amp;#39;usp_ErrorExample&amp;#39;) IS NOT NULL
DROP PROCEDURE usp_ErrorExample
GO
CREATE PROCEDURE usp_ErrorExample
AS
BEGIN
    BEGIN TRY
        
        ;WITH CounterCTE
        AS
        (
            SELECT 1 AS ValRow
            UNION ALL
            SELECT ValRow + 1
            FROM CounterCTE
            WHERE ValRow &amp;lt; 100
        )
            INSERT INTO RecordLoad(ValueColumn)
            SELECT ValRow 
            FROM CounterCTE
            
        RAISERROR(&amp;#39;This is a custom error.&amp;#39;, 16,1,1)  --THIS WILL ERROR, AND INVOKE THE CATCH BLOCK
    END TRY
    BEGIN CATCH
        EXECUTE usp_LogProcedureError
    END CATCH
END

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;As you can see, it makes writing error handling routines much much easier.&amp;nbsp; So, try to start infusing this error handling logic (or SOME type of error handling logic) into your SQL Server 2005 or 2008 code if you haven&amp;#39;t already.&amp;nbsp; It has saved me many, many hours of troubleshooting time.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://sqlservernation.com/aggbug.aspx?PostID=159" width="1" height="1"&gt;</description><category domain="http://sqlservernation.com/blogs/development/archive/tags/Intermediate+SQL/default.aspx">Intermediate SQL</category><category domain="http://sqlservernation.com/blogs/development/archive/tags/Beginner+SQL/default.aspx">Beginner SQL</category></item><item><title>Concatenating Results through SELECT statements</title><link>http://sqlservernation.com/blogs/development/archive/2009/03/20/concatenating-results-through-select-statements.aspx</link><pubDate>Fri, 20 Mar 2009 20:53:00 GMT</pubDate><guid isPermaLink="false">a3dbd76b-19b9-441f-b40a-d79e8f433d35:147</guid><dc:creator>TimChapman</dc:creator><slash:comments>10</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://sqlservernation.com/blogs/development/rsscomments.aspx?PostID=147</wfw:commentRss><comments>http://sqlservernation.com/blogs/development/archive/2009/03/20/concatenating-results-through-select-statements.aspx#comments</comments><description>&lt;p&gt;In this SQL tip I am going to show you a neat little trick which involves using a simple SELECT statement to concatenate row values into a single variable value.&lt;/p&gt;
&lt;p&gt;First, I&amp;#39;ll create a temp table named #Results for which I&amp;#39;ll store FirstName and LastName data.&lt;/p&gt;
&lt;pre style="border:1px dashed #999999;padding:5px;overflow:auto;font-size:12px;width:100%;color:#000000;line-height:14px;font-family:Andale Mono,Lucida Console,Monaco,fixed,monospace;background-color:#eeeeee;"&gt;&lt;code&gt;IF OBJECT_ID(&amp;#39;tempdb..#Results&amp;#39;) IS NOT NULL&lt;br /&gt;    DROP TABLE #Results&lt;br /&gt;&lt;br /&gt;CREATE TABLE #Results&lt;br /&gt;(&lt;br /&gt;    FirstName varchar(25), &lt;br /&gt;    LastName varchar(25)&lt;br /&gt;)&lt;br /&gt;&lt;br /&gt;GO&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This script adds some names to the temp table I just created.&lt;/p&gt;
&lt;pre style="border:1px dashed #999999;padding:5px;overflow:auto;font-size:12px;width:100%;color:#000000;line-height:14px;font-family:Andale Mono,Lucida Console,Monaco,fixed,monospace;background-color:#eeeeee;"&gt;&lt;code&gt;INSERT INTO #Results(FirstName, LastName)&lt;br /&gt;SELECT &amp;#39;Tim&amp;#39;, &amp;#39;Chapman&amp;#39; &lt;br /&gt;UNION ALL&lt;br /&gt;SELECT &amp;#39;Brandon&amp;#39;, &amp;#39;Galderisi&amp;#39; &lt;br /&gt;UNION ALL&lt;br /&gt;SELECT &amp;#39;Aaron&amp;#39;,&amp;#39;Akin&amp;#39; &lt;br /&gt;UNION ALL&lt;br /&gt;SELECT &amp;#39;Mark&amp;#39;, &amp;#39;Wills&amp;#39; &lt;br /&gt;UNION ALL&lt;br /&gt;SELECT &amp;#39;Angel&amp;#39;,&amp;#39;Eyes&amp;#39; &lt;br /&gt;UNION ALL&lt;br /&gt;SELECT &amp;#39;Daniel&amp;#39;,&amp;#39;Wilson&amp;#39;&lt;br /&gt;&lt;br /&gt;GO&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The following script does the meat of the work.&amp;nbsp; I declare a variable to hold the name values and use the variable in my SELECT statement.&amp;nbsp; The SELECT statement essentially takes every value from the #Results table and appends it to the @FullNames variable.&lt;/p&gt;
&lt;pre style="border:1px dashed #999999;padding:5px;overflow:auto;font-size:12px;width:100%;color:#000000;line-height:14px;font-family:Andale Mono,Lucida Console,Monaco,fixed,monospace;background-color:#eeeeee;"&gt;&lt;code&gt;DECLARE @FullNames VARCHAR(200)&lt;br /&gt;&lt;br /&gt;SELECT&lt;br /&gt;    @FullNames = ISNULL(@FullNames,&amp;#39;&amp;#39;) + LastName + &amp;#39;, &amp;#39; + FirstName + &amp;#39; &amp;#39;&lt;br /&gt;FROM #Results&lt;br /&gt;&lt;br /&gt;PRINT @FullNames&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The result of printing the value in the variable is below.&lt;/p&gt;
&lt;pre style="border:1px dashed #999999;padding:5px;overflow:auto;font-size:12px;width:100%;color:#000000;line-height:14px;font-family:Andale Mono,Lucida Console,Monaco,fixed,monospace;background-color:#eeeeee;"&gt;&lt;code&gt;Chapman, Tim Galderisi, Brandon Akin, Aaron Wills, Mark Eyes, Angel Wilson, Daniel &lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If I want to add a line feed to the varaible for formatting purposes, it is easy enough to do by adding the CHAR(10) character to the variable string.&amp;nbsp; This method will insert a new line feed between each value.&lt;/p&gt;
&lt;pre style="border:1px dashed #999999;padding:5px;overflow:auto;font-size:12px;width:100%;color:#000000;line-height:14px;font-family:Andale Mono,Lucida Console,Monaco,fixed,monospace;background-color:#eeeeee;"&gt;&lt;code&gt;DECLARE @FullNames VARCHAR(100)&lt;br /&gt;&lt;br /&gt;SELECT&lt;br /&gt;    @FullNames = ISNULL(@FullNames,&amp;#39;&amp;#39;) + LastName + &amp;#39;, &amp;#39; + FirstName + &amp;#39; &amp;#39; + CHAR(10)&lt;br /&gt;FROM #Results&lt;br /&gt;&lt;br /&gt;PRINT @FullNames&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Printing the contents of the @FullNames variable leaves me with the following output.&lt;/p&gt;
&lt;pre style="border:1px dashed #999999;padding:5px;overflow:auto;font-size:12px;width:100%;color:#000000;line-height:14px;font-family:Andale Mono,Lucida Console,Monaco,fixed,monospace;background-color:#eeeeee;"&gt;&lt;code&gt;Chapman, Tim &lt;br /&gt;Galderisi, Brandon &lt;br /&gt;Akin, Aaron &lt;br /&gt;Wills, Mark &lt;br /&gt;Eyes, Angel &lt;br /&gt;Wilson, Daniel &lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://sqlservernation.com/aggbug.aspx?PostID=147" width="1" height="1"&gt;</description><category domain="http://sqlservernation.com/blogs/development/archive/tags/SQL/default.aspx">SQL</category><category domain="http://sqlservernation.com/blogs/development/archive/tags/TSQL/default.aspx">TSQL</category><category domain="http://sqlservernation.com/blogs/development/archive/tags/SQL+2005/default.aspx">SQL 2005</category><category domain="http://sqlservernation.com/blogs/development/archive/tags/Intermediate+SQL/default.aspx">Intermediate SQL</category><category domain="http://sqlservernation.com/blogs/development/archive/tags/Beginner+SQL/default.aspx">Beginner SQL</category><category domain="http://sqlservernation.com/blogs/development/archive/tags/string+sql+variable+concatenate+temp+table+results/default.aspx">string sql variable concatenate temp table results</category></item><item><title>Nested Common Table Expressions</title><link>http://sqlservernation.com/blogs/development/archive/2009/03/18/nested-common-table-expressions.aspx</link><pubDate>Thu, 19 Mar 2009 02:03:00 GMT</pubDate><guid isPermaLink="false">a3dbd76b-19b9-441f-b40a-d79e8f433d35:141</guid><dc:creator>Aaron Akin</dc:creator><slash:comments>0</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://sqlservernation.com/blogs/development/rsscomments.aspx?PostID=141</wfw:commentRss><comments>http://sqlservernation.com/blogs/development/archive/2009/03/18/nested-common-table-expressions.aspx#comments</comments><description>&lt;p&gt;If you&amp;#39;ve looked up a Common Table Expressions in Books Online, you might have noticed that Microsoft says that you cannot have nested CTEs (meaning, you can&amp;#39;t define a CTE in the definition of another CTE.  Although this is technically true, you can still accomplish the same functionality in a different manner.
Here is an example of a simple CTE using the AdventureWorks database that will return a list of orders.
&lt;/p&gt;
&lt;pre style="border:1px dotted #99b1ff;overflow:auto;background-color:#ffffcf;width:100%;font-family:courier new;max-height:400px;"&gt;&lt;code&gt;USE AdventureWorks&lt;br /&gt;&lt;br /&gt;;WITH CTE_Orders (OrderID, OrderDate) AS&lt;br /&gt;(&lt;br /&gt;  SELECT PurchaseOrderID, OrderDate&lt;br /&gt;    FROM Purchasing.PurchaseOrderHeader h&lt;br /&gt;)&lt;br /&gt;&lt;br /&gt;SELECT * FROM CTE_Orders&lt;/code&gt;&lt;/pre&gt;
&lt;pre style="border:1px dotted #99b1ff;background-color:#f1f7fb;width:100%;font-family:courier new;"&gt;&lt;code&gt;OrderID     OrderDate&lt;br /&gt;----------- ----------&lt;br /&gt;1           2001-05-17             &lt;br /&gt;2           2001-05-17             &lt;br /&gt;3           2001-05-17             &lt;br /&gt;4           2001-05-17             &lt;br /&gt;5           2001-05-31             &lt;br /&gt;...&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;
What isn&amp;#39;t very clear is the use of multiple CTE definitions in one statement.  Here is an example of multiple CTEs used in one statement.  Each CTE in the statement has its own definition, and the final SELECT statement is able to query both CTEs.  This will return a list of dates that orders were made along with the number of products ordered and the total amount of those products for each date.
&lt;/p&gt;
&lt;pre style="border:1px dotted #99b1ff;overflow:auto;background-color:#ffffcf;width:100%;font-family:courier new;max-height:400px;"&gt;&lt;code&gt;;WITH CTE_OrdersDetail (OrderID, OrderDetailQty, OrderDetailAmt) AS&lt;br /&gt;(&lt;br /&gt;  SELECT PurchaseOrderID, OrderQty, LineTotal&lt;br /&gt;    FROM Purchasing.PurchaseOrderDetail&lt;br /&gt;),&lt;br /&gt;&lt;br /&gt;CTE_Orders (OrderID, OrderDate) AS&lt;br /&gt;(&lt;br /&gt;  SELECT PurchaseOrderID, OrderDate&lt;br /&gt;    FROM Purchasing.PurchaseOrderHeader h&lt;br /&gt;)&lt;br /&gt;&lt;br /&gt;SELECT o.OrderDate, SUM(od.OrderDetailQty) AS TotalOrderQty, SUM(od.OrderDetailAmt) AS TotalOrderAmt&lt;br /&gt;  FROM CTE_Orders o&lt;br /&gt;    INNER JOIN CTE_OrdersDetail od ON o.OrderID = od.OrderID&lt;br /&gt;  GROUP BY o.OrderDate&lt;br /&gt;  ORDER BY o.OrderDate&lt;/code&gt;&lt;/pre&gt;
&lt;pre style="border:1px dotted #99b1ff;background-color:#f1f7fb;width:100%;font-family:courier new;"&gt;&lt;code&gt;OrderDate  TotalOrderQty TotalOrderAmt&lt;br /&gt;---------- ------------- -------------&lt;br /&gt;2001-05-17 563           9491.518&lt;br /&gt;2001-05-31 2765          94404.303&lt;br /&gt;2002-01-14 718           39874.7685&lt;br /&gt;2002-01-15 11931         259365.2145&lt;br /&gt;2002-02-08 2768          50781.36&lt;br /&gt;...&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;
If you take this concept of having multiple CTE definitions in one statement, you can then apply it to a statement with nested CTEs.  I&amp;#39;m using the following statement to return the same result set as in the previous example, but this time I want to know the number of orders for each date that had more than 100 products ordered.
&lt;/p&gt;
&lt;pre style="border:1px dotted #99b1ff;overflow:auto;background-color:#ffffcf;width:100%;font-family:courier new;max-height:400px;"&gt;&lt;code&gt;;WITH CTE_OrdersDetail (OrderID, OrderDetailQty, OrderDetailAmt) AS&lt;br /&gt;(&lt;br /&gt;  SELECT PurchaseOrderID, OrderQty, LineTotal&lt;br /&gt;    FROM Purchasing.PurchaseOrderDetail&lt;br /&gt;),&lt;br /&gt;&lt;br /&gt;CTE_Orders (OrderID, OrderDate, IsLargeOrder) AS&lt;br /&gt;(&lt;br /&gt;  SELECT PurchaseOrderID, OrderDate&lt;br /&gt;       , (&lt;br /&gt;         SELECT CASE&lt;br /&gt;                  WHEN SUM(OrderDetailQty) &amp;gt;= 100 THEN 1&lt;br /&gt;                  ELSE 0&lt;br /&gt;                END&lt;br /&gt;           FROM CTE_OrdersDetail d&lt;br /&gt;           WHERE h.PurchaseOrderID = d.OrderID&lt;br /&gt;         )&lt;br /&gt;    FROM Purchasing.PurchaseOrderHeader h&lt;br /&gt;)&lt;br /&gt;&lt;br /&gt;SELECT o.OrderDate, SUM(od.OrderDetailQty) AS TotalOrderQty&lt;br /&gt;   , SUM(od.OrderDetailAmt) AS TotalOrderAmt, SUM(o.IsLargeOrder) AS NumLargeOrders&lt;br /&gt;  FROM CTE_Orders o&lt;br /&gt;    INNER JOIN CTE_OrdersDetail od ON o.OrderID = od.OrderID&lt;br /&gt;  GROUP BY o.OrderDate&lt;br /&gt;  ORDER BY o.OrderDate&lt;/code&gt;&lt;/pre&gt;
&lt;pre style="border:1px dotted #99b1ff;background-color:#f1f7fb;width:100%;font-family:courier new;"&gt;&lt;code&gt;OrderDate  TotalOrderQty TotalOrderAmt         NumLargeOrders&lt;br /&gt;---------- ------------- --------------------- --------------&lt;br /&gt;2001-05-17 563           9491.518              1&lt;br /&gt;2001-05-31 2765          94404.303             5&lt;br /&gt;2002-01-14 718           39874.7685            1&lt;br /&gt;2002-01-15 11931         259365.2145           26&lt;br /&gt;2002-02-08 2768          50781.36              5&lt;br /&gt;...&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;
As you can see from this example, the CTE_OrdersDetail expression is being referenced within the definition of the CTE_Orders expression, giving you the functionality of nested CTEs.  In order for a statement like this to work properly, you must declare each CTE definition before it is used in another CTE.  This means that since CTE_OrdersDetail is being reference within CTE_Orders, I must declare CTE_OrdersDetail first.  Otherwise, if the two definitions were switched, SQL would not be able to find the CTE_OrdersDetail object when it goes to verify the CTE_Orders expression.&lt;/p&gt;
&lt;p&gt;Aaron&lt;br /&gt;&lt;a href="http://aaronakinsql.wordpress.com" target="_blank"&gt;http://aaronakinsql.wordpress.com&lt;/a&gt;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://sqlservernation.com/aggbug.aspx?PostID=141" width="1" height="1"&gt;</description><category domain="http://sqlservernation.com/blogs/development/archive/tags/SQL+2008/default.aspx">SQL 2008</category><category domain="http://sqlservernation.com/blogs/development/archive/tags/SQL/default.aspx">SQL</category><category domain="http://sqlservernation.com/blogs/development/archive/tags/TSQL/default.aspx">TSQL</category><category domain="http://sqlservernation.com/blogs/development/archive/tags/CTE/default.aspx">CTE</category><category domain="http://sqlservernation.com/blogs/development/archive/tags/SQL+2005/default.aspx">SQL 2005</category><category domain="http://sqlservernation.com/blogs/development/archive/tags/Intermediate+SQL/default.aspx">Intermediate SQL</category></item></channel></rss>