<?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</title><link>http://sqlservernation.com/blogs/development/default.aspx</link><description /><dc:language>en</dc:language><generator>CommunityServer 2008.5 SP2 (Build: 40407.4157)</generator><item><title>Getting started with dynamic SQL</title><link>http://sqlservernation.com/blogs/development/archive/2009/12/07/using-stored-procedures-to-query-objects-with-dynamic-sql.aspx</link><pubDate>Mon, 07 Dec 2009 15:16:00 GMT</pubDate><guid isPermaLink="false">a3dbd76b-19b9-441f-b40a-d79e8f433d35:332</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=332</wfw:commentRss><comments>http://sqlservernation.com/blogs/development/archive/2009/12/07/using-stored-procedures-to-query-objects-with-dynamic-sql.aspx#comments</comments><description>&lt;p&gt;Sometimes you need dynamic SQL.&amp;nbsp; Sometimes you don&amp;#39;t know at run time where you&amp;#39;re querying or what fields you need to bring back, or what your criteria will be, so there will always be a place for the use of dynamic SQL.&amp;nbsp; You have to be careful how you use it, but if you do use it correctly it can be a powerful tool that you have in your tool belt.&amp;nbsp; &lt;/p&gt;
&lt;p&gt;So, today I am going to show you a nice little general purpose procedure that you can write to pass in a few object strings and execute dynamic SQL.&amp;nbsp; Im not going to go into the ins and outs of low-level dynamic SQL or what you need to do to prevent certain types of SQL injection...I&amp;#39;ll leave that discussion for a later time.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;First, lets just setup a table and insert some values into it.&amp;nbsp;&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;use tempdb

IF OBJECT_ID(&amp;#39;DynamicQueries&amp;#39;,&amp;#39;U&amp;#39;) IS NOT NULL
DROP TABLE DynamicQueries
GO
CREATE TABLE DynamicQueries
(
    ID SMALLINT NOT NULL IDENTITY(1,1) PRIMARY KEY CLUSTERED,
    Fld1 INT, 
    Fld2 INT
)
SET NOCOUNT ON

DECLARE @x SMALLINT
SET @x = 1

WHILE @x &amp;lt; 32000
BEGIN
    INSERT INTO DynamicQueries(Fld1, Fld2)
    SELECT @x % 10, @x % 7
    
    SET @x = @x + 1
END
GO
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Below is our &amp;quot;general purpose&amp;quot; dynamic SQL stored procedure.&amp;nbsp; It requires a table name and a field list (technically you could provide * for the field list) and an optional parameter for the query criteria.&amp;nbsp; It has the limitation of only querying one table for the time being, but I may expand upon that idea at a later point to allow for the addition of other tables with joins, etc.&amp;nbsp; If you wanted to make this procedure really general purpose, you could &lt;a href="http://sqlservernation.com/blogs/howtos/archive/2009/10/29/create-a-system-stored-procedure.aspx" title="System Stored Procedures"&gt;make it a system stored procedure&lt;/a&gt;.&amp;nbsp; I didn&amp;#39;t do it in this example, but you could easy just change the procedure name from usp_ to sp_, put it in the master database and mark it as a system stored procedure.&amp;nbsp; Then you&amp;#39;d be able to run this procedure in any database w/o having to worry about the database context.&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 PROCEDURE usp_GetDynamicValues
(
    @TableName NVARCHAR(255),
    @FieldList NVARCHAR(1000),
    @Criteria NVARCHAR(1000) = NULL
)
AS
BEGIN
    DECLARE @SQL NVARCHAR(MAX)
    BEGIN TRY
        SET @SQL = N&amp;#39;SELECT &amp;#39; + @FieldList + &amp;#39; FROM &amp;#39; + @TableName 
        SET @SQL = @SQL + 
                        CASE 
                            WHEN NULLIF(LTRIM(RTRIM(@Criteria)), &amp;#39;&amp;#39;) IS NULL 
                            THEN &amp;#39;&amp;#39; 
                            ELSE &amp;#39; WHERE &amp;#39; + @Criteria 
                        END
                        
        EXECUTE sp_executesql 
        @stmt = @SQL
    END TRY
    BEGIN CATCH
        PRINT &amp;#39;An error has occurred.&amp;#39;
    END CATCH
END
GO
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;Notice in the above example that I am using sp_executesql rather than the EXECUTE command. sp_executesql allow for using parameters when executing dynamic sql statements. Doing so encapsulates your parameter values which can in some instances prevent SQL injection attacks.&amp;nbsp; The parameters also lead to better plan reuse than does the EXECUTE command. So, try to always use sp_executesql over EXECUTE.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;Once the procedure is created, all that is left to do is to call it, which I am doing below.&amp;nbsp; &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;
EXECUTE usp_GetDynamicValues
@TableName = &amp;#39;DynamicQueries&amp;#39;, 
@FieldList = &amp;#39;ID, Fld2&amp;#39;, 
@Criteria = &amp;#39;ID = 4&amp;#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;While this example was relatively simple, it shows you how useful (and powerful dynamic SQL can be).&amp;nbsp; Look for future articles where I expand upon this idea and start to develop a more elaborate setup where you can really harness the power of using dynamic SQL.&lt;/p&gt;
&lt;p&gt;HTH, &lt;br /&gt;Tim&lt;/p&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=332" width="1" height="1"&gt;</description></item><item><title>Creating Indexes with IGNORE_DUP_KEY</title><link>http://sqlservernation.com/blogs/development/archive/2009/11/15/creating-indexes-with-ignore-dup-key.aspx</link><pubDate>Mon, 16 Nov 2009 01:04:00 GMT</pubDate><guid isPermaLink="false">a3dbd76b-19b9-441f-b40a-d79e8f433d35:321</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=321</wfw:commentRss><comments>http://sqlservernation.com/blogs/development/archive/2009/11/15/creating-indexes-with-ignore-dup-key.aspx#comments</comments><description>&lt;p&gt;Unique indexes and unique constraints limit the values that are allowed in a table column to unique values.&amp;nbsp; This uniqueness is almost always a result of some type of business rule.&amp;nbsp; Programming these types of business rules are usually not overcomplicated to write.&amp;nbsp; However, SQL Server has an option that you can set when creating unique indexes that allows you to control how duplicate values are handled so that you can potentially get away from writing these rules by hand.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;The unique index option, IGNORE_DUP_KEY, allows you to indicate how records that would violate a unique index should be handled.&amp;nbsp; When the option is set to OFF, any records that violate the unique constraint will result in error and the entire statement will be rolled back.&amp;nbsp; This option will prevent any records that would not have violated the unique index from getting into the table as well.&amp;nbsp; This is typically where custom programming needs to be written to only insert records that are not already in the table.&amp;nbsp; When the option is set to ON, the records that do not violate the constraint are inserted while the records that do violate the constraint are ingored.&amp;nbsp; You can see where this option may come in quite handy.&lt;/p&gt;
&lt;p&gt;To take a look at how IGNORE_DUP_KEY works, lets take a look at an example.&amp;nbsp;&amp;nbsp;I&amp;#39;ll contrast how SQL Server handles unique violations with the option set ON and OFF.&amp;nbsp; To start the example off, lets create a table.&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;TestingDuplicateValues&amp;#39;,&amp;#39;U&amp;#39;) IS NOT NULL
    DROP TABLE TestingDuplicateValues
GO
CREATE TABLE TestingDuplicateValues
(
    IDColumn SMALLINT NOT NULL
)
GO
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;Now I&amp;#39;ll create a unique index on my single column table.&amp;nbsp; By default the IGNORE_DUP_KEY option is set to OFF when a unique index is created I included it here to illustrate the TSQL syntax for how to create the index.&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 UNIQUE NONCLUSTERED INDEX idx_TestingDuplicateValues_IDColumn
ON TestingDuplicateValues(IDColumn)
WITH (IGNORE_DUP_KEY = OFF)
GO
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To test our new unique index lets create a batch to violate the unique index.&amp;nbsp; Since the IGNORE_DUP_KEY option is set to OFF, I will recieve an error telling me that I cannot insert a duplicate value into the table.&amp;nbsp; Consequently, the entire batch is rolled back.&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;INSERT INTO TestingDuplicateValues(IDColumn)
SELECT 1
UNION ALL
SELECT 1
UNION ALL
SELECT 2
G0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To validate that no records were inserted into the table, run the following query.&amp;nbsp; 0 records should be returned.&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;SELECT * 
FROM TestingDuplicateValues
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, lets drop our unique index and recreate it.&amp;nbsp; This time I will create the unique index with the IGNORE_DUP_KEY option set to ON.&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;DROP INDEX TestingDuplicateValues.idx_TestingDuplicateValues_IDColumn
GO
CREATE UNIQUE NONCLUSTERED INDEX idx_TestingDuplicateValues_IDColumn
ON TestingDuplicateValues(IDColumn)
WITH (IGNORE_DUP_KEY = ON)
GO
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When I run the same INSERT statement as before, 2 records are now inserted into the table.&amp;nbsp; The duplicate value from the statement is ignored.&amp;nbsp; No error is returned, rather I receive the informational message &amp;quot;Duplicate key was ignored.&amp;quot;&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;INSERT INTO TestingDuplicateValues(IDColumn)
SELECT 1
UNION ALL
SELECT 1
UNION ALL
SELECT 2
G0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can validate that 2 records were inserted into the table w/ the following query.&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;SELECT * 
FROM TestingDuplicateValues
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;The IGNORE_DUP_KEY is a very handy option when creating unique constraints.&amp;nbsp; However, it does not necessarily take the place of crafty programming when it is necessary.&amp;nbsp; Check out the option and determine if it works for your scenario.&amp;nbsp; If it does...GREAT, glad to help.&amp;nbsp; If not, don&amp;#39;t worry, a little programming goes a long way.&lt;/p&gt;
&lt;p&gt;Tim&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&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=321" width="1" height="1"&gt;</description><category domain="http://sqlservernation.com/blogs/development/archive/tags/index/default.aspx">index</category><category domain="http://sqlservernation.com/blogs/development/archive/tags/union+all/default.aspx">union all</category><category domain="http://sqlservernation.com/blogs/development/archive/tags/IGNORE_5F00_DUP_5F00_KEY/default.aspx">IGNORE_DUP_KEY</category><category domain="http://sqlservernation.com/blogs/development/archive/tags/unique+index/default.aspx">unique index</category><category domain="http://sqlservernation.com/blogs/development/archive/tags/sql+server/default.aspx">sql server</category></item><item><title>SQL Server 2008 Syntactic Sugar</title><link>http://sqlservernation.com/blogs/development/archive/2009/09/28/sql-server-2008-syntactic-sugar.aspx</link><pubDate>Tue, 29 Sep 2009 00:21:00 GMT</pubDate><guid isPermaLink="false">a3dbd76b-19b9-441f-b40a-d79e8f433d35:296</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=296</wfw:commentRss><comments>http://sqlservernation.com/blogs/development/archive/2009/09/28/sql-server-2008-syntactic-sugar.aspx#comments</comments><description>&lt;p&gt;As a developer, I always enjoy new TSQL language features that can save me some time and potentially effort in accomplishing the same task that I otherwise could in a different fashion.&amp;nbsp; These features aren&amp;#39;t really a big deal to me to have but are sometimes fun.&amp;nbsp; SQL Server 2008 has produced a couple of these features so I thought it might be worthwhile to take a look at them.&amp;nbsp; &lt;/p&gt;
&lt;p&gt;&lt;b&gt;Compound Operators&lt;br /&gt;&lt;/b&gt;This is a feature that exists in most common-day programming languages.&amp;nbsp; The idea is simple; why use a variable twice inside of an assignment statement when you can get away with using it just once.&amp;nbsp; So, instead of: &lt;/p&gt;
&lt;pre style="border:1px dashed #999999;padding:5px;overflow:auto;font-family:Andale Mono,Lucida Console,Monaco,fixed,monospace;color:#000000;background-color:#eeeeee;font-size:12px;line-height:14px;width:100%;"&gt;&lt;code&gt;DECLARE @IntegerVariable INT =  5&lt;br /&gt;SET @IntegerVariable = @IntegerVariable + 1&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The result of the above statement is 6 as you&amp;#39;d expect.&amp;nbsp; However, I did sneak in a new feature that I&amp;#39;d yet to mention.&amp;nbsp; In the variable declaration I actually assigned a value to the variable in the declaration.&amp;nbsp; This is also a new feature in SQL Server 2008.&amp;nbsp; I kinda like not having to break my assignment out onto a new line when I am giving a variable an initial value.&lt;/p&gt;
&lt;p&gt;You can alter the statement a bit to compound the operator and accomplish the same task.&amp;nbsp; This syntax is in the form of:&lt;/p&gt;
&lt;pre style="border:1px dashed #999999;padding:5px;overflow:auto;font-family:Andale Mono,Lucida Console,Monaco,fixed,monospace;color:#000000;background-color:#eeeeee;font-size:12px;line-height:14px;width:100%;"&gt;&lt;code&gt;DECLARE @IntegerVariable INT =  5&lt;br /&gt;SET @IntegerVariable += 1&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This syntax works pretty much anywhere you&amp;#39;d normally use a variable twice inside an assignment operator.&amp;nbsp; Its neat....not critical, but definitely a feature that is nice to have.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Row Constructors&lt;/b&gt;&lt;br /&gt;A Row Constructor in SQL Server 2008 is really nothing more than an easier way to INSERT rows of values into a table at the same time using the VALUES portion of the INSERT statement.&amp;nbsp; Technically, you can do the same thing using a SELECT and UNION ALL statement in versions prior to 2008.&amp;nbsp; I still think it is a kinda neat new feature...&lt;/p&gt;
&lt;p&gt;To make use of the new feature I&amp;#39;ll need a table that I can INSERT some values into.&lt;/p&gt;
&lt;pre style="border:1px dashed #999999;padding:5px;overflow:auto;font-family:Andale Mono,Lucida Console,Monaco,fixed,monospace;color:#000000;background-color:#eeeeee;font-size:12px;line-height:14px;width:100%;"&gt;&lt;code&gt;IF OBJECT_ID(&amp;#39;InsertRowConstructor&amp;#39;) IS NOT NULL&lt;br /&gt;DROP TABLE InsertRowConstructor&lt;br /&gt;GO&lt;br /&gt;CREATE TABLE InsertRowConstructor&lt;br /&gt;(&lt;br /&gt;    Field1 INT, Field2 INT&lt;br /&gt;)&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once my table is created (or dropped and recreated), I add some values to it.&amp;nbsp; The only difference with the new way to use the VALUES clause is that you can add as many tuples of data to it as you&amp;#39;d like.&amp;nbsp; You&amp;#39;ll need to wrap parentheses around the tuples and seperate them with commas.&lt;/p&gt;
&lt;pre style="border:1px dashed #999999;padding:5px;overflow:auto;font-family:Andale Mono,Lucida Console,Monaco,fixed,monospace;color:#000000;background-color:#eeeeee;font-size:12px;line-height:14px;width:100%;"&gt;&lt;code&gt;INSERT INTO InsertRowConstructor(Field1, Field2)&lt;br /&gt;VALUES  (1,2), &lt;br /&gt;        (2,3), &lt;br /&gt;        (3,4), &lt;br /&gt;        (4,5), &lt;br /&gt;        (5,6)&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Of course, I could have accomplished this using a SELECT statement with a UNION (ALL) command:&lt;/p&gt;
&lt;pre style="border:1px dashed #999999;padding:5px;overflow:auto;font-family:Andale Mono,Lucida Console,Monaco,fixed,monospace;color:#000000;background-color:#eeeeee;font-size:12px;line-height:14px;width:100%;"&gt;&lt;code&gt;INSERT INTO InsertRowConstructor(Field1, Field2)&lt;br /&gt;SELECT 1,2&lt;br /&gt;UNION ALL&lt;br /&gt;SELECT 2,3&lt;br /&gt;UNION ALL &lt;br /&gt;SELECT 3,4&lt;br /&gt;UNION ALL &lt;br /&gt;SELECT 4,5&lt;br /&gt;UNION ALL&lt;br /&gt;SELECT 5,6&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The SELECT approach isn&amp;#39;t quite as fun, but works the same way.&lt;/p&gt;
&lt;p&gt;There is also a new MERGE feature in SQL 2008 that I won&amp;#39;t discuss here.&amp;nbsp; You can read more about &lt;a title="MERGE" href="http://blogs.techrepublic.com.com/datacenter/?p=194"&gt;my MERGE blog post &lt;/a&gt;on TechRepublic.&amp;nbsp; In a way, its the large piece of syntactic sugar because it allows you to combine INSERT, UPDATE, and DELETE statements into one statement.&amp;nbsp; However, this feature can make those situations where you need to write seperate INSERT and UPDATE statements based on record existence so much easier.&amp;nbsp; A GREAT feature in my opinion.&lt;/p&gt;
&lt;p&gt;Tim&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=296" width="1" height="1"&gt;</description><category domain="http://sqlservernation.com/blogs/development/archive/tags/merge+sql+2008+operator+variable+compound/default.aspx">merge sql 2008 operator variable compound</category></item><item><title>Pivot Rolling Periods : Dynamic, But Not!</title><link>http://sqlservernation.com/blogs/development/archive/2009/09/17/pivot-rolling-periods-dynamic-but-not.aspx</link><pubDate>Fri, 18 Sep 2009 03:53:00 GMT</pubDate><guid isPermaLink="false">a3dbd76b-19b9-441f-b40a-d79e8f433d35:283</guid><dc:creator>Kevin Cross</dc:creator><slash:comments>1</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://sqlservernation.com/blogs/development/rsscomments.aspx?PostID=283</wfw:commentRss><comments>http://sqlservernation.com/blogs/development/archive/2009/09/17/pivot-rolling-periods-dynamic-but-not.aspx#comments</comments><description>&lt;p&gt;In the Article,&amp;nbsp;Pivot Rolling Periods Without Using Dynamic T-SQL (&lt;a target="_blank" title="Pivot Rolling Periods Without Using Dynamic T-SQL" href="http://experts-exchange.com/viewArticle.jsp?aid=654"&gt;experts-exchange.com/viewArticle.jsp?aid=654&lt;/a&gt;),&amp;nbsp;I explored the pivot of moving date based reports without the use of dynamic T-SQL, but still allow for re-use month after month without re-writing of code.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Here is another introductory tidbit to SQL Server 2005&amp;#39;s PIVOT (&lt;a href="http://technet.microsoft.com/en-us/library/ms177410.aspx"&gt;technet.microsoft.com/en-us/library/ms177410.aspx&lt;/a&gt;). &amp;nbsp;We will&amp;nbsp;embrace the limitations of the PIVOT keyword requiring hard coded values / column names, but still provide nice reports that can be used in views or quick queries without getting into multi-line dynamic code. &amp;nbsp;&lt;/p&gt;
&lt;p&gt;To play along, you can download and run the following DDL and data insert SQL script:&lt;/p&gt;
&lt;p&gt;(Please visit the site to view this media)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Okay, now we can play. &amp;nbsp;The following starts out by getting data greater or equal to last January 1st at midnight &amp;quot;dateadd(year, datediff(year, 0, getdate())-1, 0)&amp;quot;. &amp;nbsp;Then we employ a date function to make our datetime value non-specific to year (i.e., month(bk_date) will yield 1-12). &amp;nbsp;For this report, we want to have a year over year analysis of data by month so we also use year(bk_date) to get our grouped column heading which doesn&amp;#39;t affect us in the PIVOT since it will remain as data.&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;;with bks( byear, bmonth, amount )
as
(
select year(bk_date), month(bk_date), bk_amount
from bookings
where bk_date &amp;gt;= dateadd(year, datediff(year, 0, getdate())-1, 0)
)
select byear as &amp;quot;Sales Year&amp;quot;,
coalesce(&amp;quot;1&amp;quot;, 0.00) as January,
coalesce(&amp;quot;2&amp;quot;, 0.00) as February,
coalesce(&amp;quot;3&amp;quot;, 0.00) as March,
coalesce(&amp;quot;4&amp;quot;, 0.00) as April,
coalesce(&amp;quot;5&amp;quot;, 0.00) as May,
coalesce(&amp;quot;6&amp;quot;, 0.00) as June,
coalesce(&amp;quot;7&amp;quot;, 0.00) as July,
coalesce(&amp;quot;8&amp;quot;, 0.00) as August,
coalesce(&amp;quot;9&amp;quot;, 0.00) as September,
coalesce(&amp;quot;10&amp;quot;, 0.00) as October,
coalesce(&amp;quot;11&amp;quot;, 0.00) as November,
coalesce(&amp;quot;12&amp;quot;, 0.00) as December
from bks
pivot( sum(amount) for bmonth 
       in (&amp;quot;1&amp;quot;,&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;,&amp;quot;4&amp;quot;,&amp;quot;5&amp;quot;,&amp;quot;6&amp;quot;,&amp;quot;7&amp;quot;,&amp;quot;8&amp;quot;,&amp;quot;9&amp;quot;,&amp;quot;10&amp;quot;,&amp;quot;11&amp;quot;,&amp;quot;12&amp;quot;) ) pvt
order by &amp;quot;Sales Year&amp;quot; desc&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Results will look similar to below:&lt;/p&gt;
&lt;p&gt;&lt;a href="http://sqlservernation.com/cfs-file.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/development.PivotRollingPeriodsDynamicButNot/1881.pivot_2D00_1_2D00_results.jpg"&gt;&lt;img border="0" src="http://sqlservernation.com/resized-image.ashx/__size/550x0/__key/CommunityServer.Blogs.Components.WeblogFiles/development.PivotRollingPeriodsDynamicButNot/1881.pivot_2D00_1_2D00_results.jpg" style="border:0;" alt="Pivot with Explicit Select List" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;The above uses the month number, since it has to be typed multiple times as use explicit select list to use functions like coalesce(). &amp;nbsp;This is more important when doing advanced pivoting like with multiple columns (&lt;a target="_blank" title="Broaden Your Horizons : Pivot Again!" href="http://experts-exchange.com/viewArticle.jsp?aid=1537"&gt;experts-exchange.com/viewArticle.jsp?aid=1537&lt;/a&gt;); however, since we have a simple case here where null may be okay as it quickly answers the question of a missing data point versus a very poor $0.00 booking month, we can use a much simplified code:
&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;with bks(&amp;quot;Sales Year&amp;quot;, bmonthname, amount)
as
(
select year(bk_date), datename(month, bk_date), bk_amount
from bookings
where bk_date &amp;gt;= dateadd(year, datediff(year, 0, getdate())-1, 0)
)
select *
from bks
pivot( sum(amount) for bmonthname
       in (January, February, March, April, May, June,
           July, August, September, October, November, December) ) pvt
order by &amp;quot;Sales Year&amp;quot; desc&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This version looks similar to this:&lt;/p&gt;
&lt;p&gt;&lt;a href="http://sqlservernation.com/cfs-file.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/development.PivotRollingPeriodsDynamicButNot/6082.pivot_2D00_2_2D00_results.jpg"&gt;&lt;img src="http://sqlservernation.com/resized-image.ashx/__size/550x0/__key/CommunityServer.Blogs.Components.WeblogFiles/development.PivotRollingPeriodsDynamicButNot/6082.pivot_2D00_2_2D00_results.jpg" border="0" alt="" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;As you will see, we haven&amp;#39;t lost any value in the actual data and gained less code to maintain and a clear cut distinction between 0&amp;#39;s and no data as already stated. &amp;nbsp;We utilize the datename() function here to strip out the year and day as we did before, but instead of just a number we return full month. &amp;nbsp;For consistency, you can use left( datename( month, bk_date ), 3 ) and you will get &amp;#39;Jan&amp;#39;, &amp;#39;Feb&amp;#39;, &amp;#39;Mar&amp;#39;, etc. &amp;nbsp;It is normally not a good practice to use wilcard (*) for selecting records, but since we explicitly defined the columns we wanted in the CTE, &amp;#39;select *&amp;#39; is very safe here as we are still protected from changes in the underlying table structure as we are selecting all from the CTE and not the base table.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Notice, we still following the same syntax on the pivot, just changed the actual values in play.&lt;/p&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:#eee;border:#999999 1px dashed;padding:5px;"&gt;&lt;code&gt;pivot( aggregate_function( column_to_aggregate ) for column_to_pivot
       in ( pivot_value1 [, pivot_value2 [, ... pivot_valuen]] )
) pivot_alias&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;i&gt;Hopefully that was helpful!&lt;/i&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;This was my first entry, so hopefully you liked it. &amp;nbsp;Will work on adding more to it if folks are interested as I use these kinds of queries frequently for things like item inventory turns over or dependent / independent usage over the past 12 months, so have played with it a bit.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;-- Kevin&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=283" 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/Beginner+SQL/default.aspx">Beginner SQL</category></item><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>0</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>Stripping the time from a DATETIME value</title><link>http://sqlservernation.com/blogs/development/archive/2009/03/19/stripping-the-time-from-a-datetime-value.aspx</link><pubDate>Fri, 20 Mar 2009 02:39:00 GMT</pubDate><guid isPermaLink="false">a3dbd76b-19b9-441f-b40a-d79e8f433d35:145</guid><dc:creator>Aaron Akin</dc:creator><slash:comments>1</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://sqlservernation.com/blogs/development/rsscomments.aspx?PostID=145</wfw:commentRss><comments>http://sqlservernation.com/blogs/development/archive/2009/03/19/stripping-the-time-from-a-datetime-value.aspx#comments</comments><description>&lt;p&gt;Many times there is a need to strip the time off of a DATETIME value so you are left with the date only.  I&amp;#39;ve seen a few different ways to handle this.  To clarify, when I say a DATETIME value with no time, I really mean midnight (00:00:00.000) on that date.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Converting to VARCHAR&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;One way to remove the time from a DATETIME value is to convert it to a VARCHAR data type.  This is handy when you need the date returned as a VARCHAR value and when you need the option to return the date in any number of different formats, with or without the century (i.e. 2009 vs 09).  The full list of the different formats that are available using CONVERT is located in &lt;a target="_blank" href="http://msdn.microsoft.com/en-us/library/ms187928.aspx"&gt;Books Online&lt;/a&gt;.  Here is an example of a few the different formats.&lt;/p&gt;
&lt;pre style="background-color:#ffffcf;width:100%;font-family:courier new;max-height:400px;overflow:auto;border:#99b1ff 1px dotted;"&gt;&lt;code&gt;DECLARE @dt DATETIME
SET @dt = GETDATE()

SELECT CONVERT(VARCHAR,@dt,1) AS Date1
     , CONVERT(VARCHAR,@dt,101) AS Date101
     , CONVERT(VARCHAR,@dt,12) AS Date12
     , CONVERT(VARCHAR,@dt,112) AS Date112&lt;/code&gt;&lt;/pre&gt;
&lt;pre style="background-color:#f1f7fb;width:100%;font-family:courier new;border:#99b1ff 1px dotted;"&gt;&lt;code&gt;Date1      Date101    Date12     Date112
---------- ---------- ---------- ----------
03/19/09   03/19/2009 090319     20090319&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;One thing to consider when comparing a DATETIME value to another DATETIME value that has been converted to VARCHAR, you should always convert it back to DATETIME.  This is called an explicit conversion when you use CAST or CONVERT to change a value to a different data type.  The comparison will still work properly without the explicit conversion, but this will cause SQL Server to convert it at runtime, called an implicit conversion, putting unneccesary overhead on the server.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Using DATEADD and DATEDIFF&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Another way to remove the time from a DATETIME value is to use a combination of the DATEADD and DATEDIFF functions.  This is a good way to not only strip the time off of a particular DATETIME value, but you can also use that value to get other dates without the time.  Here is an example of a few different dates that can be returned using the current date as a reference point.&lt;/p&gt;
&lt;pre style="background-color:#ffffcf;width:100%;font-family:courier new;max-height:400px;overflow:auto;border:#99b1ff 1px dotted;"&gt;&lt;code&gt;DECLARE @dt DATETIME
SET @dt = GETDATE()

SELECT DATEADD(DAY,DATEDIFF(DAY,0,@dt),0) AS Today
     , DATEADD(DAY,DATEDIFF(DAY,0,@dt),-1) AS Yesterday
     , DATEADD(MONTH,DATEDIFF(MONTH,0,@dt),0) AS FirstOfThisMonth
     , DATEADD(MONTH,DATEDIFF(MONTH,-1,@dt),-1) AS LastOfThisMonth
     , DATEADD(MONTH,DATEDIFF(MONTH,0,@dt),-1) AS LastOfPrevMonth
     , DATEADD(YEAR,DATEDIFF(YEAR,0,@dt),0) AS FirstOfYear&lt;/code&gt;&lt;/pre&gt;
&lt;pre style="background-color:#f1f7fb;width:100%;font-family:courier new;border:#99b1ff 1px dotted;"&gt;&lt;code&gt;Today               Yesterday           FirstOfThisMonth    LastOfThisMonth     LastOfPrevMonth     FirstOfYear
------------------- ------------------- ------------------- ------------------- ------------------- -------------------
2009-03-19 00:00:00 2009-03-18 00:00:00 2009-02-28 00:00:00 2009-03-31 00:00:00 2009-02-28 00:00:00 2009-01-01 

00:00:00&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;ODBC Date/Time Escape Clauses&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Another interesting method I found recently involves ODBC date and time escape clauses.  I don&amp;#39;t really know much about these, but I thought they were intriguing.  There are 3 extensions, one for date (d), time (t), and timestamp (t).  The only one I thought might be useful was the time extension.  Here&amp;#39;s an example of using this to retrieve today&amp;#39;s date with no time value.&lt;/p&gt;
&lt;pre style="background-color:#ffffcf;width:100%;font-family:courier new;max-height:400px;overflow:auto;border:#99b1ff 1px dotted;"&gt;&lt;code&gt;SELECT {t &amp;#39;00:00:00&amp;#39;} AS Today&lt;/code&gt;&lt;/pre&gt;
&lt;pre style="background-color:#f1f7fb;width:100%;font-family:courier new;border:#99b1ff 1px dotted;"&gt;&lt;code&gt;Today
-------------------
2009-03-19 00:00:00&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I could see this being handy if you only ever wanted to get the current date at midnight, but I haven&amp;#39;t found a way to pass in a variable or column to this, so it is limited to the current date only.  Just for kicks, here&amp;#39;s how you would use the date and timestamp escape clauses.&lt;/p&gt;
&lt;pre style="background-color:#ffffcf;width:100%;font-family:courier new;max-height:400px;overflow:auto;border:#99b1ff 1px dotted;"&gt;&lt;code&gt;SELECT {ts &amp;#39;2009-03-19 00:00:00&amp;#39;}
     , {d &amp;#39;2009-03-19&amp;#39;}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Both of these will return today&amp;#39;s date at midnight as well.  I&amp;#39;d be interested to see if anyone knows any other uses for these.&lt;/p&gt;
&lt;p&gt;Aaron&lt;br /&gt;&lt;a target="_blank" href="http://aaronakinsql.wordpress.com"&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=145" width="1" height="1"&gt;</description><category domain="http://sqlservernation.com/blogs/development/archive/tags/TSQL/default.aspx">TSQL</category><category domain="http://sqlservernation.com/blogs/development/archive/tags/Beginner+SQL/default.aspx">Beginner SQL</category></item><item><title>Finding the major version number of your SQL Server instance</title><link>http://sqlservernation.com/blogs/development/archive/2009/03/18/finding-the-major-version-number-of-your-sql-server-instance.aspx</link><pubDate>Thu, 19 Mar 2009 02:32:00 GMT</pubDate><guid isPermaLink="false">a3dbd76b-19b9-441f-b40a-d79e8f433d35:142</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=142</wfw:commentRss><comments>http://sqlservernation.com/blogs/development/archive/2009/03/18/finding-the-major-version-number-of-your-sql-server-instance.aspx#comments</comments><description>&lt;p&gt;Occasionally, I need to determine which version of SQL Server I am connected to within a script so that I can write the proper code for that version.  I&amp;rsquo;m never really concerned with the build number or which service pack I have installed.  Instead, all I want is the major version number (7, 8, 9, 10, etc).&lt;/p&gt;
&lt;p&gt;One of the difficult things about something that seems like it should be so simple is that some of the ways to check the version number only exist on certain versions.  Other ways give you way more info than you really need.&lt;/p&gt;
&lt;p&gt;Recently, I was digging through a system stored procedure, which is always the best place to find cool code, and I came across the undocumented system function @@MICROSOFTVERSION which is used internally by Microsoft.
It returns a seemingly random number, but you can use a bitwise AND operator to find the major version number.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&lt;span style="color:blue;"&gt;SELECT&lt;/span&gt; @@MICROSOFTVERSION / 0x01000000&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&lt;/code&gt;This will return the following based on your version of SQL Server:&lt;/p&gt;
&lt;p&gt;7 &amp;nbsp; (SQL 7)&lt;br /&gt;8 &amp;nbsp; (SQL 2000)&lt;br /&gt;9 &amp;nbsp; (SQL 2005)&lt;br /&gt;10 (SQL 2008)&lt;/p&gt;
&lt;p&gt;This will work on any version of SQL Server as of now, but since it is undocumented, it&amp;rsquo;s always possible that it could be removed in the future.&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=142" 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/Version/default.aspx">Version</category><category domain="http://sqlservernation.com/blogs/development/archive/tags/Beginner+SQL/default.aspx">Beginner SQL</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>