<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:copyright="http://blogs.law.harvard.edu/tech/rss" xmlns:image="http://purl.org/rss/1.0/modules/image/" version="2.0">
    <channel>
        <title>Jeff's SQL Server Blog</title>
        <link>http://weblogs.sqlteam.com/jeffs/Default.aspx</link>
        <description>Random Thoughts &amp; Cartesian Products with Microsoft SQL Server</description>
        <language>en-US</language>
        <copyright>Jeff Smith</copyright>
        <managingEditor>smith_jeffreyt@yahoo.com</managingEditor>
        <generator>Subtext Version 1.9.4.0</generator>
        <image>
            <title>Jeff's SQL Server Blog</title>
            <url>http://weblogs.sqlteam.com/images/RSS2Image.gif</url>
            <link>http://weblogs.sqlteam.com/jeffs/Default.aspx</link>
            <width>77</width>
            <height>60</height>
        </image>
        <atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" href="http://feeds.feedburner.com/JeffsSqlServerWeblog" type="application/rss+xml" /><feedburner:emailServiceId xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0">JeffsSqlServerWeblog</feedburner:emailServiceId><feedburner:feedburnerHostname xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0">http://feedburner.google.com</feedburner:feedburnerHostname><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com" /><item>
            <title>Processing an OLAP cube with a T-SQL Stored Procedure</title>
            <link>http://weblogs.sqlteam.com/jeffs/archive/2008/10/27/process-olap-cube-with-t-sql.aspx</link>
            <description>Here's a simple SQL Server stored procedure that you can call to process an OLAP cube using T-SQL.  The parameters should be self-explanatory.  To me, this is a little easier and more flexible than processing using DTS packages.&lt;br /&gt;
&lt;br /&gt;
&lt;div style="background: white none repeat scroll 0% 0%; font-family: Verdana; font-size: 8pt; color: black; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; margin-left: 40px;"&gt;
&lt;p style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;create procedure &lt;/span&gt;ProcessCube&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;    @Database &lt;span style="color: blue;"&gt;varchar&lt;/span&gt;(100),&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;    @Cube &lt;span style="color: blue;"&gt;varchar&lt;/span&gt;(100),&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;    @Partition &lt;span style="color: blue;"&gt;varchar&lt;/span&gt;(100)  = &lt;span style="color: blue;"&gt;null&lt;/span&gt;, &lt;span style="color: green;"&gt;-- If NULL, process the entire Cube&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;    @Server &lt;span style="color: blue;"&gt;varchar&lt;/span&gt;(100) = &lt;span style="color: rgb(163, 21, 21);"&gt;'localhost'&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;as&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt; &lt;/p&gt;
&lt;p style="margin: 0px;"&gt;&lt;span style="color: green;"&gt;/* &lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;    &lt;span style="color: green;"&gt;Author:    &lt;/span&gt;    &lt;span style="color: green;"&gt;Jeff Smith&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;    &lt;span style="color: green;"&gt;Version:    10/27/2008&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;&lt;span style="color: green;"&gt;*/&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt; &lt;/p&gt;
&lt;p style="margin: 0px;"&gt;&lt;span style="color: green;"&gt;/* variables used to store object handles */&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;declare &lt;/span&gt;@o_svr &lt;span style="color: blue;"&gt;int&lt;/span&gt;, @o_db &lt;span style="color: blue;"&gt;int&lt;/span&gt;, @o_cube &lt;span style="color: blue;"&gt;int&lt;/span&gt;, @o_part &lt;span style="color: blue;"&gt;int&lt;/span&gt;, @o_mds &lt;span style="color: blue;"&gt;int&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;declare &lt;/span&gt;@hr &lt;span style="color: blue;"&gt;int&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt; &lt;/p&gt;
&lt;p style="margin: 0px;"&gt;&lt;span style="color: green;"&gt;/* different cube processing options. &lt;/span&gt; &lt;span style="color: green;"&gt;This SP uses "default" */&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;declare &lt;/span&gt;@PROCESS_DEFAULT &lt;span style="color: blue;"&gt;int&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;declare &lt;/span&gt;@PROCESS_FULL &lt;span style="color: blue;"&gt;int&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;declare &lt;/span&gt;@PROCESS_REFRESH_DATA &lt;span style="color: blue;"&gt;int&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt; &lt;/p&gt;
&lt;p style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;set &lt;/span&gt;@PROCESS_DEFAULT = 0&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;set &lt;/span&gt;@PROCESS_FULL = 1&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;set &lt;/span&gt;@PROCESS_REFRESH_DATA = 2&lt;/p&gt;
&lt;p style="margin: 0px;"&gt; &lt;/p&gt;
&lt;p style="margin: 0px;"&gt;&lt;span style="color: green;"&gt;-- create a DSO.Server object:&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;exec &lt;/span&gt;@hr = sp_OACreate &lt;span style="color: rgb(163, 21, 21);"&gt;'DSO.Server'&lt;/span&gt;, @o_svr out&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;if &lt;/span&gt;@hr &amp;lt;&amp;gt; 0&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;    &lt;span style="color: blue;"&gt;begin&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;        &lt;span style="color: blue;"&gt;print &lt;/span&gt;&lt;span style="color: rgb(163, 21, 21);"&gt;'Error at create server:'&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;        &lt;span style="color: blue;"&gt;exec &lt;/span&gt;sp_OAGetErrorInfo @o_svr&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;        &lt;span style="color: blue;"&gt;goto &lt;/span&gt;cleanup&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;    &lt;span style="color: blue;"&gt;end&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt; &lt;/p&gt;
&lt;p style="margin: 0px;"&gt;&lt;span style="color: green;"&gt;-- Connect to the server:&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;exec &lt;/span&gt;@hr = sp_OAMethod @o_svr, &lt;span style="color: rgb(163, 21, 21);"&gt;'Connect'&lt;/span&gt;, &lt;span style="color: blue;"&gt;null&lt;/span&gt;, @Server&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;if &lt;/span&gt;@hr &amp;lt;&amp;gt; 0&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;    &lt;span style="color: blue;"&gt;begin&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;        &lt;span style="color: blue;"&gt;print &lt;/span&gt;&lt;span style="color: rgb(163, 21, 21);"&gt;'Error at connect to server:'&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;        &lt;span style="color: blue;"&gt;exec &lt;/span&gt;sp_OAGetErrorInfo @o_svr&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;        &lt;span style="color: blue;"&gt;goto &lt;/span&gt;cleanup&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;    &lt;span style="color: blue;"&gt;end&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt; &lt;/p&gt;
&lt;p style="margin: 0px;"&gt;&lt;span style="color: green;"&gt;-- Get the MDStores property from the Server:&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;exec &lt;/span&gt;@hr = sp_OAGetProperty @o_svr,&lt;span style="color: rgb(163, 21, 21);"&gt;'MDStores'&lt;/span&gt;, @o_mds OUT&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;if &lt;/span&gt;@hr &amp;lt;&amp;gt; 0&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;    &lt;span style="color: blue;"&gt;begin&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;        &lt;span style="color: blue;"&gt;print &lt;/span&gt;&lt;span style="color: rgb(163, 21, 21);"&gt;'Error at get getting Server MDStores:'&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;        &lt;span style="color: blue;"&gt;exec &lt;/span&gt;sp_OAGetErrorInfo @o_svr&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;        &lt;span style="color: blue;"&gt;goto &lt;/span&gt;cleanup&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;    &lt;span style="color: blue;"&gt;end&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt; &lt;/p&gt;
&lt;p style="margin: 0px;"&gt;&lt;span style="color: green;"&gt;-- Get the database from the MDStores:&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;exec &lt;/span&gt;@hr = sp_OAGetProperty @o_mds,&lt;span style="color: rgb(163, 21, 21);"&gt;'Item'&lt;/span&gt;,@o_db OUT,@Database&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;if &lt;/span&gt;@hr &amp;lt;&amp;gt; 0&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;    &lt;span style="color: blue;"&gt;begin&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;        &lt;span style="color: blue;"&gt;print &lt;/span&gt;&lt;span style="color: rgb(163, 21, 21);"&gt;'Error at get database:'&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;        &lt;span style="color: blue;"&gt;exec &lt;/span&gt;sp_OAGetErrorInfo @o_mds&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;        &lt;span style="color: blue;"&gt;goto &lt;/span&gt;cleanup&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;    &lt;span style="color: blue;"&gt;end&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt; &lt;/p&gt;
&lt;p style="margin: 0px;"&gt;&lt;span style="color: green;"&gt;-- get the MDStores property from the database:&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;exec &lt;/span&gt;sp_OADestroy @o_mds&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;exec &lt;/span&gt;@hr = sp_OAGetProperty @o_db,&lt;span style="color: rgb(163, 21, 21);"&gt;'MDStores'&lt;/span&gt;, @o_mds OUT&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;if &lt;/span&gt;@hr &amp;lt;&amp;gt; 0&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;    &lt;span style="color: blue;"&gt;begin&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;        &lt;span style="color: blue;"&gt;print &lt;/span&gt;&lt;span style="color: rgb(163, 21, 21);"&gt;'Error at get database MDStores:'&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;        &lt;span style="color: blue;"&gt;exec &lt;/span&gt;sp_OAGetErrorInfo @o_db&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;        &lt;span style="color: blue;"&gt;goto &lt;/span&gt;cleanup&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;    &lt;span style="color: blue;"&gt;end&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt; &lt;/p&gt;
&lt;p style="margin: 0px;"&gt;&lt;span style="color: green;"&gt;-- get the Cube from the MDStores&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;exec &lt;/span&gt;@hr = sp_OAGetProperty @o_mds,&lt;span style="color: rgb(163, 21, 21);"&gt;'Item'&lt;/span&gt;,@o_cube OUT, @Cube&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;if &lt;/span&gt;@hr &amp;lt;&amp;gt; 0&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;    &lt;span style="color: blue;"&gt;begin&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;        &lt;span style="color: blue;"&gt;print &lt;/span&gt;&lt;span style="color: rgb(163, 21, 21);"&gt;'Error at get Cube:'&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;        &lt;span style="color: blue;"&gt;exec &lt;/span&gt;sp_OAGetErrorInfo @o_mds&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;        &lt;span style="color: blue;"&gt;goto &lt;/span&gt;cleanup&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;    &lt;span style="color: blue;"&gt;end&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt; &lt;/p&gt;
&lt;p style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;if &lt;/span&gt;@Partition &lt;span style="color: blue;"&gt;is null &lt;/span&gt; &lt;span style="color: green;"&gt;-- Process the entire Cube, not just a single partition&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;    &lt;span style="color: blue;"&gt;begin&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;        &lt;span style="color: blue;"&gt;exec &lt;/span&gt;@hr = sp_OAMethod @o_cube, &lt;span style="color: rgb(163, 21, 21);"&gt;'Process'&lt;/span&gt;, &lt;span style="color: blue;"&gt;null&lt;/span&gt;, @PROCESS_DEFAULT&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;        &lt;span style="color: blue;"&gt;if &lt;/span&gt;@hr &amp;lt;&amp;gt; 0&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;            &lt;span style="color: blue;"&gt;begin&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;                &lt;span style="color: blue;"&gt;print &lt;/span&gt;&lt;span style="color: rgb(163, 21, 21);"&gt;'Error at process Cube:'&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;                &lt;span style="color: blue;"&gt;exec &lt;/span&gt;sp_OAGetErrorInfo @o_cube&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;                &lt;span style="color: blue;"&gt;goto &lt;/span&gt;cleanup&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;            &lt;span style="color: blue;"&gt;end&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;    &lt;span style="color: blue;"&gt;end&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt; &lt;/p&gt;
&lt;p style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;else    &lt;/span&gt;&lt;span style="color: green;"&gt;-- just process the specified Partition&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt; &lt;/p&gt;
&lt;p style="margin: 0px;"&gt;    &lt;span style="color: blue;"&gt;begin&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;        &lt;span style="color: green;"&gt;-- Get the MDStores property of the Cube:&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;        &lt;span style="color: blue;"&gt;exec &lt;/span&gt;sp_OADestroy @o_mds&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;        &lt;span style="color: blue;"&gt;exec &lt;/span&gt;@hr = sp_OAGetProperty @o_cube,&lt;span style="color: rgb(163, 21, 21);"&gt;'MDStores'&lt;/span&gt;, @o_mds OUT&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;        &lt;span style="color: blue;"&gt;if &lt;/span&gt;@hr &amp;lt;&amp;gt; 0&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;            &lt;span style="color: blue;"&gt;begin&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;                &lt;span style="color: blue;"&gt;print &lt;/span&gt;&lt;span style="color: rgb(163, 21, 21);"&gt;'Error at get Cube MDStores:'&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;                &lt;span style="color: blue;"&gt;exec &lt;/span&gt;sp_OAGetErrorInfo @o_cube&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;                &lt;span style="color: blue;"&gt;goto &lt;/span&gt;cleanup&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;            &lt;span style="color: blue;"&gt;end&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt; &lt;/p&gt;
&lt;p style="margin: 0px;"&gt;        &lt;span style="color: green;"&gt;-- &lt;/span&gt; &lt;span style="color: green;"&gt;Get the partition to process:&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;        &lt;span style="color: blue;"&gt;exec &lt;/span&gt;@hr = sp_OAGetProperty @o_mds,&lt;span style="color: rgb(163, 21, 21);"&gt;'Item'&lt;/span&gt;,@o_part OUT, @Partition&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;        &lt;span style="color: blue;"&gt;if &lt;/span&gt;@hr &amp;lt;&amp;gt; 0&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;            &lt;span style="color: blue;"&gt;begin&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;                &lt;span style="color: blue;"&gt;print &lt;/span&gt;&lt;span style="color: rgb(163, 21, 21);"&gt;'Error at get Parition:'&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;                &lt;span style="color: blue;"&gt;exec &lt;/span&gt;sp_OAGetErrorInfo @o_mds&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;                &lt;span style="color: blue;"&gt;goto &lt;/span&gt;cleanup&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;            &lt;span style="color: blue;"&gt;end&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt; &lt;/p&gt;
&lt;p style="margin: 0px;"&gt;        &lt;span style="color: green;"&gt;-- Process the partition:&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;        &lt;span style="color: blue;"&gt;exec &lt;/span&gt;@hr = sp_OAMethod @o_part, &lt;span style="color: rgb(163, 21, 21);"&gt;'Process'&lt;/span&gt;, &lt;span style="color: blue;"&gt;null&lt;/span&gt;, @PROCESS_DEFAULT&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;        &lt;span style="color: blue;"&gt;if &lt;/span&gt;@hr &amp;lt;&amp;gt; 0&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;            &lt;span style="color: blue;"&gt;begin&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;                &lt;span style="color: blue;"&gt;print &lt;/span&gt;&lt;span style="color: rgb(163, 21, 21);"&gt;'Error at process Partition:'&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;                &lt;span style="color: blue;"&gt;exec &lt;/span&gt;sp_OAGetErrorInfo @o_part&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;                &lt;span style="color: blue;"&gt;goto &lt;/span&gt;cleanup&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;            &lt;span style="color: blue;"&gt;end&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;    &lt;span style="color: blue;"&gt;end&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt; &lt;/p&gt;
&lt;p style="margin: 0px;"&gt;&lt;span style="color: green;"&gt;-- And unlock all objects on the server:&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;exec &lt;/span&gt;@hr = sp_OAMethod @o_svr, &lt;span style="color: rgb(163, 21, 21);"&gt;'UnlockAllObjects'&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;if &lt;/span&gt;@hr &amp;lt;&amp;gt; 0&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;    &lt;span style="color: blue;"&gt;begin&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;        &lt;span style="color: blue;"&gt;print &lt;/span&gt;&lt;span style="color: rgb(163, 21, 21);"&gt;'Error at unlock all server objects:'&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;        &lt;span style="color: blue;"&gt;exec &lt;/span&gt;sp_OAGetErrorInfo @o_svr&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;        &lt;span style="color: blue;"&gt;goto &lt;/span&gt;cleanup&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;    &lt;span style="color: blue;"&gt;end&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt; &lt;/p&gt;
&lt;p style="margin: 0px;"&gt;cleanup:&lt;/p&gt;
&lt;p style="margin: 0px;"&gt; &lt;/p&gt;
&lt;p style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;if &lt;/span&gt;@o_mds &lt;span style="color: blue;"&gt;is not null exec &lt;/span&gt;sp_OADestroy @o_mds&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;if &lt;/span&gt;@o_Part &lt;span style="color: blue;"&gt;is not null exec &lt;/span&gt;sp_OADestroy @o_Part&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;if &lt;/span&gt;@o_cube &lt;span style="color: blue;"&gt;is not null exec &lt;/span&gt;sp_OADestroy @o_cube&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;if &lt;/span&gt;@o_db &lt;span style="color: blue;"&gt;is not null exec &lt;/span&gt;sp_OADestroy @o_db&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;&lt;span style="color: blue;"&gt;if &lt;/span&gt;@o_svr &lt;span style="color: blue;"&gt;is not null exec &lt;/span&gt;sp_OADestroy @o_svr&lt;/p&gt;
&lt;/div&gt;&lt;img src="http://weblogs.sqlteam.com/jeffs/aggbug/60746.aspx" width="1" height="1" /&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/joRKoH_VhiftZye6wYEzTFxWESE/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/joRKoH_VhiftZye6wYEzTFxWESE/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/joRKoH_VhiftZye6wYEzTFxWESE/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/joRKoH_VhiftZye6wYEzTFxWESE/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=WyHNddoZo1U:AlXGS8sVGqI:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=WyHNddoZo1U:AlXGS8sVGqI:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?i=WyHNddoZo1U:AlXGS8sVGqI:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=WyHNddoZo1U:AlXGS8sVGqI:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?i=WyHNddoZo1U:AlXGS8sVGqI:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=WyHNddoZo1U:AlXGS8sVGqI:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?i=WyHNddoZo1U:AlXGS8sVGqI:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=WyHNddoZo1U:AlXGS8sVGqI:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</description>
            <dc:creator>Jeff Smith</dc:creator>
            <guid>http://weblogs.sqlteam.com/jeffs/archive/2008/10/27/process-olap-cube-with-t-sql.aspx</guid>
            <pubDate>Mon, 27 Oct 2008 19:26:05 GMT</pubDate>
            <wfw:comment>http://weblogs.sqlteam.com/jeffs/comments/60746.aspx</wfw:comment>
            <comments>http://weblogs.sqlteam.com/jeffs/archive/2008/10/27/process-olap-cube-with-t-sql.aspx#feedback</comments>
            <slash:comments>5</slash:comments>
            <wfw:commentRss>http://weblogs.sqlteam.com/jeffs/comments/commentRss/60746.aspx</wfw:commentRss>
            <trackback:ping>http://weblogs.sqlteam.com/jeffs/services/trackbacks/60746.aspx</trackback:ping>
        </item>
        <item>
            <title>I'm back ... with some news!</title>
            <link>http://weblogs.sqlteam.com/jeffs/archive/2008/10/16/60734.aspx</link>
            <description>I apologize for not posting any new content in quite some time, but now I am back and will soon start posting on a (hopefully!) regular basis once again.&lt;br /&gt;
&lt;br /&gt;
The reason for my hiatus was a pretty good one, though: I recently became a father with the birth of my son Benjamin on October 2, 2008!   He is doing great and already knows that he should always do his formatting at the client and never within the database.  He's a natural!&lt;br /&gt;
&lt;br /&gt;
As if that wasn't enough, I also found out recently I have been named as a &lt;span style="font-weight: bold;"&gt;2009 SQL Server MVP&lt;/span&gt;!  Now I can &lt;span style="font-style: italic;"&gt;finally &lt;/span&gt;get a decent seat at a restaurant by pulling the "do you know who I am?" routine.  In all seriousness, though, I am very honored and excited by the award and I intend to live up to it in 2009 by continuing to update this blog and help users out in the &lt;a href="http://www.sqlteam.com/forums"&gt;SQLTeam forums&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Thank you to everyone who reads this blog and participates in the discussions, and I promise more updates are coming soon.  If you have any specific topics or ideas for a blog post that you think I may be able to cover effectively, just &lt;a href="http://weblogs.sqlteam.com/jeffs/contact.aspx"&gt;let me know&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Best Regards,&lt;br /&gt;
&lt;br /&gt;
Jeff Smith&lt;br /&gt;
SQL Server MVP&lt;br /&gt;
 &lt;br /&gt;
(Hey -- I like the way that looks!)&lt;img src="http://weblogs.sqlteam.com/jeffs/aggbug/60734.aspx" width="1" height="1" /&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/pVvUz_t6MjCxSJa_8hO8lfLbP6U/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/pVvUz_t6MjCxSJa_8hO8lfLbP6U/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/pVvUz_t6MjCxSJa_8hO8lfLbP6U/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/pVvUz_t6MjCxSJa_8hO8lfLbP6U/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=RN9ugEkWTbE:sslt0bn_yaQ:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=RN9ugEkWTbE:sslt0bn_yaQ:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?i=RN9ugEkWTbE:sslt0bn_yaQ:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=RN9ugEkWTbE:sslt0bn_yaQ:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?i=RN9ugEkWTbE:sslt0bn_yaQ:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=RN9ugEkWTbE:sslt0bn_yaQ:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?i=RN9ugEkWTbE:sslt0bn_yaQ:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=RN9ugEkWTbE:sslt0bn_yaQ:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</description>
            <dc:creator>Jeff Smith</dc:creator>
            <guid>http://weblogs.sqlteam.com/jeffs/archive/2008/10/16/60734.aspx</guid>
            <pubDate>Thu, 16 Oct 2008 20:04:35 GMT</pubDate>
            <wfw:comment>http://weblogs.sqlteam.com/jeffs/comments/60734.aspx</wfw:comment>
            <comments>http://weblogs.sqlteam.com/jeffs/archive/2008/10/16/60734.aspx#feedback</comments>
            <slash:comments>9</slash:comments>
            <wfw:commentRss>http://weblogs.sqlteam.com/jeffs/comments/commentRss/60734.aspx</wfw:commentRss>
            <trackback:ping>http://weblogs.sqlteam.com/jeffs/services/trackbacks/60734.aspx</trackback:ping>
        </item>
        <item>
            <title>SQL Server 2005 Foreign Key Constraints: SET NULL and SET DEFAULT</title>
            <link>http://weblogs.sqlteam.com/jeffs/archive/2008/08/13/sql-server-set-null-set-default-foreign-key-constraints.aspx</link>
            <description>Most people know about cascading updates and deletes, but did you know there are two other foreign key constraint options you can use to maintain referential integrity?&lt;br /&gt;
&lt;br /&gt;
Read all about them in &lt;a href="http://www.sqlteam.com/article/using-set-null-and-set-default-with-foreign-key-constraints"&gt;my latest article over at SQLTeam.com&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
These features, introduced with SQL Server 2005, haven't got a lot of publicity, but they can be very useful.  I just used the SET NULL option recently for the first time (inspiring me to put together an article on it) and it works great.&lt;img src="http://weblogs.sqlteam.com/jeffs/aggbug/60682.aspx" width="1" height="1" /&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/FvSzKmxUQBziELeSWJjiO6Z3eMI/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/FvSzKmxUQBziELeSWJjiO6Z3eMI/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/FvSzKmxUQBziELeSWJjiO6Z3eMI/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/FvSzKmxUQBziELeSWJjiO6Z3eMI/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=OfF3Sao4srY:dS7glcxd56w:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=OfF3Sao4srY:dS7glcxd56w:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?i=OfF3Sao4srY:dS7glcxd56w:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=OfF3Sao4srY:dS7glcxd56w:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?i=OfF3Sao4srY:dS7glcxd56w:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=OfF3Sao4srY:dS7glcxd56w:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?i=OfF3Sao4srY:dS7glcxd56w:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=OfF3Sao4srY:dS7glcxd56w:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</description>
            <dc:creator>Jeff Smith</dc:creator>
            <guid>http://weblogs.sqlteam.com/jeffs/archive/2008/08/13/sql-server-set-null-set-default-foreign-key-constraints.aspx</guid>
            <pubDate>Wed, 13 Aug 2008 13:56:49 GMT</pubDate>
            <wfw:comment>http://weblogs.sqlteam.com/jeffs/comments/60682.aspx</wfw:comment>
            <comments>http://weblogs.sqlteam.com/jeffs/archive/2008/08/13/sql-server-set-null-set-default-foreign-key-constraints.aspx#feedback</comments>
            <slash:comments>4</slash:comments>
            <wfw:commentRss>http://weblogs.sqlteam.com/jeffs/comments/commentRss/60682.aspx</wfw:commentRss>
            <trackback:ping>http://weblogs.sqlteam.com/jeffs/services/trackbacks/60682.aspx</trackback:ping>
        </item>
        <item>
            <title>Database Column Names != Report Headings</title>
            <link>http://weblogs.sqlteam.com/jeffs/archive/2008/08/06/60669.aspx</link>
            <description>Always remember that the column names returned in a result set do not have to be the same as what you eventually output at your presentation layer.   &lt;br /&gt;
&lt;br /&gt;
For example, suppose you have stored procedure that accepts a @CurrentYear parameter and returns a sales variance between the current year and the previous year for each customer.  I often see programmers struggling with writing dynamic SQL to produce output like this:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;CustomerID   2008 Total    2007 Total   Variance&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;----------   ----------    ----------   --------&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;ABC          $100          $50          $50&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;DEF          $200          $250         -$50&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;/div&gt;
&lt;br /&gt;
That is, the names of the columns &lt;span style="font-style: italic;"&gt;vary &lt;/span&gt;based on the data; that is &lt;span style="font-style: italic;"&gt;not &lt;/span&gt;a good way to return data from your database!  A much better result set to return is simply this:&lt;br /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;CustomerID   CurrentYear   PrevYear   Variance&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;----------   ----------    ---------  --------&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;ABC          $100          $50        $50&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;DEF          $200          $250       -$50&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
Notice that with that set of columns, no dynamic SQL is needed, and the column names returned are always constant regardless of the value of the @CurrYear parameter.   &lt;br /&gt;
&lt;br /&gt;
As mentioned, the fact that your data set has columns labelled "CurrentYear" and "PrevYear" does not mean that you cannot re-label them any way that you like on your report or web page.  &lt;br /&gt;
&lt;br /&gt;
If your client code called the stored procedure and provided a @CurrentYear parameter, then it knows exactly what "CurrentYear" and "PrevYear" represent, and you can easily label the columns in the final result exactly as needed with simple formulas or a few lines of code.  &lt;br /&gt;
&lt;br /&gt;
Remember that in the world of relational database programming, table names and column names should be constant -- only the data itself should change.   Focus on returning consistently structured data from your database, and let your client applications handle the labeling of columns to make them look nice. &lt;img src="http://weblogs.sqlteam.com/jeffs/aggbug/60669.aspx" width="1" height="1" /&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/1oHXCyA7ARMXuVamQT4Xmv5PGkg/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/1oHXCyA7ARMXuVamQT4Xmv5PGkg/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/1oHXCyA7ARMXuVamQT4Xmv5PGkg/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/1oHXCyA7ARMXuVamQT4Xmv5PGkg/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=EANkcwAJVqQ:791UDA6CXY8:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=EANkcwAJVqQ:791UDA6CXY8:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?i=EANkcwAJVqQ:791UDA6CXY8:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=EANkcwAJVqQ:791UDA6CXY8:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?i=EANkcwAJVqQ:791UDA6CXY8:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=EANkcwAJVqQ:791UDA6CXY8:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?i=EANkcwAJVqQ:791UDA6CXY8:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=EANkcwAJVqQ:791UDA6CXY8:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</description>
            <dc:creator>Jeff Smith</dc:creator>
            <guid>http://weblogs.sqlteam.com/jeffs/archive/2008/08/06/60669.aspx</guid>
            <pubDate>Wed, 06 Aug 2008 15:43:26 GMT</pubDate>
            <wfw:comment>http://weblogs.sqlteam.com/jeffs/comments/60669.aspx</wfw:comment>
            <comments>http://weblogs.sqlteam.com/jeffs/archive/2008/08/06/60669.aspx#feedback</comments>
            <slash:comments>3</slash:comments>
            <wfw:commentRss>http://weblogs.sqlteam.com/jeffs/comments/commentRss/60669.aspx</wfw:commentRss>
            <trackback:ping>http://weblogs.sqlteam.com/jeffs/services/trackbacks/60669.aspx</trackback:ping>
        </item>
        <item>
            <title>How To Calculate the Number of Week Days Between two Dates</title>
            <link>http://weblogs.sqlteam.com/jeffs/archive/2008/07/31/week-days-between-two-dates.aspx</link>
            <description>If the start date and end date are both week days, then the total number of week days in between is simply:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;(total difference in days) - (total difference in weeks) * 2&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
or&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt; DateDiff(dd, @start, @end) - DateDiff(ww, @start, @end)*2&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
... since the DateDiff() function with weeks returns the number of week "boundaries" that are crossed; i.e., the number of weekends.&lt;br /&gt;
&lt;br /&gt;
If you have a table of holidays, then you can simply subtract them out as well:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px; font-family: Courier New;"&gt;DateDiff(dd, @start, @end) - &lt;br /&gt;
DateDiff(ww, @start, @end)*2 -  &lt;br /&gt;
(select count(*) from holidays where holiday_date between @start and @end)&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
Now, what if the start day or the end day is on a weekend?  In that case, you need to define what to do in those situations in your requirements. &lt;br /&gt;
&lt;br /&gt;
For example, if the start date is Sunday, Nov 20th, and the end day is Monday, Nov 21st -- how many week days are between those dates? There's no universal correct answer; it could be 0, or 1, or perhaps even "undefined" (null) depending on your needs.&lt;img src="http://weblogs.sqlteam.com/jeffs/aggbug/60662.aspx" width="1" height="1" /&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/_EDy2DVK-3Lp98ymeru5pyGp7lE/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/_EDy2DVK-3Lp98ymeru5pyGp7lE/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/_EDy2DVK-3Lp98ymeru5pyGp7lE/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/_EDy2DVK-3Lp98ymeru5pyGp7lE/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=ePvsVrWKlrQ:G2q0WHk_ss0:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=ePvsVrWKlrQ:G2q0WHk_ss0:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?i=ePvsVrWKlrQ:G2q0WHk_ss0:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=ePvsVrWKlrQ:G2q0WHk_ss0:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?i=ePvsVrWKlrQ:G2q0WHk_ss0:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=ePvsVrWKlrQ:G2q0WHk_ss0:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?i=ePvsVrWKlrQ:G2q0WHk_ss0:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=ePvsVrWKlrQ:G2q0WHk_ss0:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</description>
            <dc:creator>Jeff Smith</dc:creator>
            <guid>http://weblogs.sqlteam.com/jeffs/archive/2008/07/31/week-days-between-two-dates.aspx</guid>
            <pubDate>Thu, 31 Jul 2008 16:39:43 GMT</pubDate>
            <wfw:comment>http://weblogs.sqlteam.com/jeffs/comments/60662.aspx</wfw:comment>
            <comments>http://weblogs.sqlteam.com/jeffs/archive/2008/07/31/week-days-between-two-dates.aspx#feedback</comments>
            <slash:comments>11</slash:comments>
            <wfw:commentRss>http://weblogs.sqlteam.com/jeffs/comments/commentRss/60662.aspx</wfw:commentRss>
            <trackback:ping>http://weblogs.sqlteam.com/jeffs/services/trackbacks/60662.aspx</trackback:ping>
        </item>
        <item>
            <title>Convert input explicitly at your client; don't rely on the database to "figure it out"</title>
            <link>http://weblogs.sqlteam.com/jeffs/archive/2008/07/24/60657.aspx</link>
            <description>&lt;p&gt;A common mistake beginners make when working with SQL is trying to format their output at the database layer, as opposed to simply doing this at the presentation layer (i.e., client application, reporting tool, web page, etc).  I've covered that &lt;a href="http://weblogs.sqlteam.com/jeffs/archive/2007/08/29/SQL-Dates-and-Times.aspx"&gt;quite&lt;/a&gt; &lt;a href="http://weblogs.sqlteam.com/jeffs/archive/2007/04/13/format-date-sql-server.aspx"&gt;a bit&lt;/a&gt; in various blog posts, but I've only &lt;a href="http://weblogs.sqlteam.com/jeffs/archive/2006/07/21/10728.aspx"&gt;touched upon&lt;/a&gt; another similar issue which I feel is equally as important and also commonly mishandled.&lt;/p&gt;
&lt;p&gt;In the &lt;a href="http://www.sqlteam.com/forums/"&gt;SqlTeam forums&lt;/a&gt;, I often see code that accepts DateTime input in the form of a string value (say, from a TextBox on a web form) and uploads that value to the database written like this:&lt;span style="font-family: Courier New;"&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;SqlCommand c = new SqlCommand();&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;c.CommandText = "insert into SomeTable (DateCol) values ('" + txtDate.Text + "')";&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;c.ExecuteNonQuery();&lt;/span&gt;&lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Now, I think that hopefully even most beginners will agree that this is bad code.   The primary issue, of course, is &lt;a href="http://en.wikipedia.org/wiki/SQL_injection"&gt;SQL Injection&lt;/a&gt;.  Avoiding SQL Injection is very easy to do using Parameters.  So, let's say that you rewrite this code using parameters like this:&lt;/p&gt;
&lt;p style="margin-left: 40px; font-family: Courier New;"&gt;SqlCommand c = new SqlCommand();&lt;br /&gt;
c.CommandText = "insert into SomeTable (DateCol) values (@DateVal)";&lt;br /&gt;
c.Parameters.AddWithValue("@DateVal",txtDate.Text);&lt;br /&gt;
c.ExecuteNonQuery();&lt;/p&gt;
&lt;p&gt;Looking at that, it seems we have done quite a bit better and should be happy with the code.  It works well, and no injection is possible.  But there is still an issue!  Why?  The txtDate.Text property returns a &lt;em&gt;string&lt;/em&gt;, not a DateTime!   And, since we are not setting the data type of the parameter explicitly, the parameter being passed is a string (i.e., VARCHAR or NVARCHAR) value, &lt;em&gt;not a true DateTime value&lt;/em&gt;.  This means that SQL Server must implicitly cast your string to a DateTime to store it in your table, and this may or may not work successfully, or as expected, depending on how the string is formatted.  &lt;/p&gt;
&lt;p&gt;I've said it over and over and I'll say it again:  The concept of formatting dates should never be something that your database code should ever worry about.  The database layer should be accepting DateTime data from clients, and returning DateTime data to your clients. Where and how the client got the data before passing it to the database, or what the client does with the data in terms of formatting after receiving it from the database is of no concern to the database itself.  &lt;/p&gt;
&lt;p&gt;So, we might decide that to fix this, we can simply declare the data type of the parameter explicitly:&lt;/p&gt;
&lt;p style="margin-left: 40px; font-family: Courier New;"&gt;SqlCommand c = new SqlCommand();&lt;br /&gt;
c.CommandText = "insert into SomeTable (DateCol) values (@DateVal)";&lt;br /&gt;
c.Parameters.Add("@DateVal", SqlDbType.DateTime).Value = txtDate.Text;&lt;br /&gt;
c.ExecuteNonQuery();&lt;/p&gt;
&lt;p&gt;It appears that now we are in good shape, right?  Actually -- no! There is &lt;em&gt;still &lt;/em&gt;an implicit conversion happening, because we are still passing a string value -- the txtDate.Text property -- to the parameter, not a true DateTime!&lt;/p&gt;
&lt;p&gt;Let's try one more time.  How can we avoid these implicit conversions?  The answer that question is always the same: Convert explicitly!  Your client application is fully capable of handling the parsing, validation, and conversion of that string to a true DateTime value, so go ahead and do it:&lt;/p&gt;
&lt;p style="margin-left: 40px;"&gt;&lt;font face="Courier New"&gt;DateTime dateval = DateTime.Parse(txtDate.Text); // plus more code to validate, of course&lt;/font&gt;&lt;/p&gt;
&lt;p style="margin-left: 40px;"&gt;&lt;font face="Courier New"&gt;SqlCommand c = new SqlCommand();&lt;br /&gt;
c.CommandText = "insert into SomeTable (DateCol) values (@DateVal)";&lt;br /&gt;
c.Parameters.Add("@DateVal", SqlDbType.DateTime).Value = dateval;&lt;br /&gt;
c.ExecuteNonQuery();&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;Now we are in business!  Before we even create the SqlCommand object, we have a true DateTime value that we are ready to pass along to SQL Server.  Our SQL code doesn't need to worry about formatting, parsing, converting, or anything -- it is being passed a completely valid piece of data with the correct type.   In short, we can now be sure that whatever value we came up with for the date in our client code is &lt;em&gt;exactly &lt;/em&gt;the value that will be stored in our database.  That's the idea, right?&lt;/p&gt;
&lt;p&gt;So, please, don't rely on your database code to validate your input.    Don't just pass along generic string data and "hope" that at the end of the day the database can "handle it".  Eliminate the chance of anything going wrong and write your code to explicitly cast and convert and validate any and all input before the database even comes into the picture.   &lt;br /&gt;
&lt;/p&gt;&lt;img src="http://weblogs.sqlteam.com/jeffs/aggbug/60657.aspx" width="1" height="1" /&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/MH0bGCvyGvnsE-OtsOUavTU_vTc/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/MH0bGCvyGvnsE-OtsOUavTU_vTc/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/MH0bGCvyGvnsE-OtsOUavTU_vTc/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/MH0bGCvyGvnsE-OtsOUavTU_vTc/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=yTEyFOamv_I:UIivUTRqnPo:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=yTEyFOamv_I:UIivUTRqnPo:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?i=yTEyFOamv_I:UIivUTRqnPo:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=yTEyFOamv_I:UIivUTRqnPo:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?i=yTEyFOamv_I:UIivUTRqnPo:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=yTEyFOamv_I:UIivUTRqnPo:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?i=yTEyFOamv_I:UIivUTRqnPo:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=yTEyFOamv_I:UIivUTRqnPo:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</description>
            <dc:creator>Jeff Smith</dc:creator>
            <guid>http://weblogs.sqlteam.com/jeffs/archive/2008/07/24/60657.aspx</guid>
            <pubDate>Thu, 24 Jul 2008 13:12:33 GMT</pubDate>
            <wfw:comment>http://weblogs.sqlteam.com/jeffs/comments/60657.aspx</wfw:comment>
            <comments>http://weblogs.sqlteam.com/jeffs/archive/2008/07/24/60657.aspx#feedback</comments>
            <slash:comments>11</slash:comments>
            <wfw:commentRss>http://weblogs.sqlteam.com/jeffs/comments/commentRss/60657.aspx</wfw:commentRss>
            <trackback:ping>http://weblogs.sqlteam.com/jeffs/services/trackbacks/60657.aspx</trackback:ping>
        </item>
        <item>
            <title>The MailBag --- Super-Sized Edition!  String Parsing, Crosstabs, SQL Injection, and more.</title>
            <link>http://weblogs.sqlteam.com/jeffs/archive/2008/07/16/60652.aspx</link>
            <description>OK, boys and girls, it's time for the &lt;a href="http://weblogs.sqlteam.com/jeffs/contact.aspx"&gt;mailbag&lt;/a&gt;!  There's lots of stuff to cover, so let's get to it!&lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-weight: bold;"&gt;---&lt;br /&gt;
&lt;br /&gt;
Greg E &lt;/span&gt;writes:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-style: italic;"&gt;Hello Jeff,&lt;/span&gt;&lt;br style="font-style: italic;" /&gt;
&lt;br style="font-style: italic;" /&gt;
&lt;span style="font-style: italic;"&gt;I just found your blog and wanted to know if you could point me in the right direction or possibly toss me a solution.&lt;/span&gt;&lt;br style="font-style: italic;" /&gt;
&lt;br style="font-style: italic;" /&gt;
&lt;span style="font-style: italic;"&gt;I am looking at a badly formed telelphone number column in a MS SQL Server db. Entries contain '(555) 555-1212' or '555.555.1212, etc. Do you know how I would go about stripping out unwanted characters from the telephone number?&lt;/span&gt;&lt;br style="font-style: italic;" /&gt;
&lt;br style="font-style: italic;" /&gt;
&lt;span style="font-style: italic;"&gt;Thanks for the brain cycles.&lt;/span&gt;&lt;br style="font-style: italic;" /&gt;
&lt;/div&gt;
&lt;br /&gt;
Greg -- A simple UDF should do the trick for you.  For example, something like this:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;create function NumbersOnly(@txt varchar(1000))&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;returns varchar(100)&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;as&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;begin&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    declare @i int&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    declare @ret varchar(100)&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;   &lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    select @i = 1, @ret = ''&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    while (@i &amp;lt;= len(@txt))&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;        select @ret = @ret + case when substring(@txt,@i,1) like '[0-9]' &lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;                                  then substring(@txt,@i,1) else '' &lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;                             end,&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;               @i = @i + 1&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    return @ret&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;end&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;/div&gt;
&lt;br /&gt;
With that, you can write something like this:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;select ID, dbo.NumbersOnly(PhoneColumn) as PhoneNumbersOnly&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;from YourTable&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;/div&gt;
&lt;br /&gt;
Over at &lt;a href="http://www.sqlteam.com/forums/forum.asp?FORUM_ID=11"&gt;SQLTeam's script library forum&lt;/a&gt;, there is a &lt;a href="http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=79083"&gt;thread&lt;/a&gt; with a bunch of parsing functions that you may find useful if your needs are more complex.&lt;br /&gt;
&lt;br /&gt;
And, in case you missed it, be sure to read &lt;a href="http://weblogs.sqlteam.com/jeffs/archive/2008/06/11/golden-rule-of-data-manipulation.aspx"&gt;this post&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
In response to my &lt;a href="http://weblogs.sqlteam.com/jeffs/archive/2007/06/26/60240.aspx"&gt;blog post on passing arrays to stored procedures&lt;/a&gt;, &lt;span style="font-weight: bold;"&gt;Juan &lt;/span&gt;writes:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-style: italic;"&gt;I know is not the right solution, but I have to say it for the sake of completeness of the discussion: if the amount of items in your "array parameter" is limited (say, for example 5 or 10 items), you can always use optional parameters (i.e. assign them to null when declaring them in the SP), then insert them in a temp table or do whatever you want with them, without using dynamic,nor xml, nor string manipulation.&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
Great point, something I missed in my article entirely.  Sometimes, it may make sense to declare @Val1, @Val2, ... @ValN parameters if there aren't too many and there's a clearly defined limit.  Thanks for bringing that up, Juan.  The simplest solution is usually the best, and in some cases that's probably all you need.  You still have clearly defined parameters with strong typing and no parsing, and those are the main issues with CSV parameters that I wanted to avoid.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-weight: bold;"&gt; Marc &lt;/span&gt;writes:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px; font-style: italic;"&gt;We have three tables.  They all share the same "type" of primary key: let's say ActivityCode.  I need to pull data using an ActivityCode, but there is a catch.  If table 1 has the data, I want to use it.  If table 2 has the data and Table 1 does not I want to use Table 2.  If table 3 has the data and Table 1 and Table 2 does not, I want to use that.  The ActivityCode can be found in both Table 1 and Table 2.  Once I determine which table i am using I will need to do several other inner and/or outer joins with other tables.  I am using JDBC.  I want to be able to do this using a single SQL statement, but I am willing to use multiple statements if it makes more sense.  I just need to keep it to a single transaction under JDBC.&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
Marc -- I think what you are looking for is described &lt;a href="http://weblogs.sqlteam.com/jeffs/archive/2007/04/03/Conditional-Joins.aspx"&gt;here&lt;/a&gt;.  The key is to OUTER JOIN to all of your tables, and then use a CASE expression to determine which of those joined tables has the data you need.&lt;br /&gt;
&lt;br /&gt;
--&lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-weight: bold;"&gt;Mary &lt;/span&gt;writes:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-style: italic;"&gt;I struggled with a thorny SQL problem all day yesterday and found your &lt;a href="http://weblogs.sqlteam.com/jeffs/archive/2007/04/30/60192.aspx"&gt;post on set based thinking&lt;/a&gt; very helpful.  I needed to write an update query that updated a table with many records with the same key from a table with the key and the corresponding new value.  The table with new values didn't exist - I had to derive it from a different table showing the key, new value and date (the new value changed over time.)&lt;/span&gt;&lt;br style="font-style: italic;" /&gt;
&lt;br style="font-style: italic;" /&gt;
&lt;span style="font-style: italic;"&gt;Your observations that one needs to break the problem down into its simplest components helped me realize something else. &lt;/span&gt;&lt;br style="font-style: italic;" /&gt;
&lt;br style="font-style: italic;" /&gt;
&lt;span style="font-style: italic;"&gt;I made the classic rookie error of grabbing some code that did a similar type of update and try to hack it into my solution.  When I finally realized I was going in the wrong direction (because my solution was getting messier and messier), I went back to the beginning.&lt;/span&gt;&lt;br style="font-style: italic;" /&gt;
&lt;br style="font-style: italic;" /&gt;
&lt;span style="font-style: italic;"&gt;I defined the problem in its simplest terms and learned I could do a simple "update  A set A.value = B.value from A join B on B.key = A.key" .&lt;/span&gt;&lt;br style="font-style: italic;" /&gt;
&lt;br style="font-style: italic;" /&gt;
&lt;span style="font-style: italic;"&gt;I didn't realize I could update from but once the problem was simply defined a quick question to one of our senior engineers resulted in a quick answer leading to an elegant solution.  The whole thing was completed in less than half an hour.&lt;/span&gt;&lt;br style="font-style: italic;" /&gt;
&lt;br style="font-style: italic;" /&gt;
&lt;span style="font-style: italic;"&gt;The moral of the story:  Define the problem first!  Don't even think about syntax until you have written a clear, concise spec from the problem just defined.  Then if you find yourself spending an inordinate amount of time and/or the solution seems too messy or seems to run too long - google or talk to your colleagues.&lt;/span&gt;&lt;br style="font-style: italic;" /&gt;
&lt;br style="font-style: italic;" /&gt;
&lt;span style="font-style: italic;"&gt;Thanks for a great blog; your post made me realize it's more about how we think than throwing code at the problem - the code should be the last thing!&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
Thanks, Mary!  I'm glad I would be of assistance.  The "moral" that you wrote says it all. 90% of programming isn't writing code at all, it is simply defining what your code will do -- and that's always the hardest part!  &lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
In &lt;a href="http://weblogs.sqlteam.com/jeffs/archive/2007/09/10/group-by-month-sql.aspx"&gt;response to my post on grouping by month&lt;/a&gt;, &lt;span style="font-weight: bold;"&gt;Mark &lt;/span&gt;writes:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px; font-style: italic;"&gt;I'm so close! I've tried all the things in this article, but can't seem to do what I want to do. I've been tearing my hair out for days! Here's what I'm trying to do.&lt;br /&gt;
&lt;br /&gt;
Basically I need a sql procedure that looks at an invoicing table that totals amounts by month/year and quarter at the same time. Here's how my table looks:&lt;br /&gt;
&lt;br /&gt;
Project ID  Date      Amount&lt;br /&gt;
1                3/11/08    10.00&lt;br /&gt;
1                4/18/08    10.00&lt;br /&gt;
1                6/22/08    10.00&lt;br /&gt;
2                3/01/08    10.00&lt;br /&gt;
2                9/15/08    10.00&lt;br /&gt;
&lt;br /&gt;
I would like the output to have dynamic columns, so an output may look like:&lt;br /&gt;
Project ID  Jan'08  Feb'08  Mar'08  Q1'08  Apr'08  May'08  Jun'08  Q2'08  Jul'08  Aug'08  Sep'08  Q3'08&lt;br /&gt;
1              0.00    0.00      10.00    10.00  10.00    0.00      10.00  20.00  0.00    0.00      0.00      0.00&lt;br /&gt;
2              0.00    0.00      10.00    10.00  0.00      0.00      0.00    0.00  0.00    0.00      10.00    10.00&lt;br /&gt;
&lt;br /&gt;
I would like the query to know if there was no value in Jan &amp;amp;amp; Feb'08, but still list all the months in Q1.&lt;br /&gt;
&lt;br /&gt;
I'm not opposed to using a calendar table, but would like to try to avoid it if possible.&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-style: italic;"&gt;Any help would be greatly appreciated!&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
Hi Mark -- First off, never be afraid to use a calendar table!  There is nothing hacky or unusual or tricky about them, they can make your life much easier, your code much shorter, and everything much more efficient.  If grouping by month or some other time period is important to your reporting,  then defining those months in a permanent, nicely indexed table makes perfect sense.&lt;br /&gt;
&lt;br /&gt;
In this case, though, since you are outputting one &lt;span style="font-style: italic;"&gt;column &lt;/span&gt;per month for a single year, I recommend to simply use CASE expressions to "cross tab" your data.  You can alias your columns as M1,M2,M3...M12 and Q1-Q4 so that no matter what year you are running the report for, your columns will be consistently named, and you can let your presentation layer handle outputting nice column headers with the current year/month for each one.&lt;br /&gt;
&lt;br /&gt;
So, all you really need is something like this:&lt;br /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt; select projectID, Y as [Year],&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;   sum(case when m=1 then amount else 0 end) as M1,&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;   sum(case when m=2 then amount else 0 end) as M2,&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-family: Courier New;"&gt;   sum(case when m=3 then amount else 0 end) as M3,&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-family: Courier New;"&gt;   sum(case when m in (1,2,3) then amount else 0 end) as Q1,&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;   ...&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;   sum(case when m=12 then amount else 0 end) as M12,&lt;br /&gt;
  sum(amount) as Total&lt;br style="font-family: Courier New;" /&gt;
&lt;/span&gt; &lt;span style="font-family: Courier New;"&gt; from&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt; (&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;   select projectID, Amount, DatePart(Month, [Date]) as M, DatePart(Year, [Date]) as Y&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-family: Courier New;"&gt;  from YourTable&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;   where [Date] &amp;gt;= @StartDate and Date &amp;lt; @EndDate&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt; ) x&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
Of course, you'd define @StartDate and @endDate as '01-01-2008' and '01-01-2009', respectively.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
In &lt;a href="http://weblogs.sqlteam.com/jeffs/archive/2007/12/13/select-distinct-order-by-error.aspx#39101"&gt;this comment&lt;/a&gt;, &lt;span style="font-weight: bold;"&gt;Stewy &lt;/span&gt;writes:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px; font-style: italic;"&gt;I have an issue with both DISTINCT and GROUP BY.&lt;br /&gt;
&lt;br /&gt;
The issue is that using either one, the results comes back ordered as if using order by.&lt;br /&gt;
&lt;br /&gt;
I need the unique results in the order they are in the database. How can I do this? Thanks&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
Stewy -- Relational databases have no obligation to store data in any specific order, or to keep track of the order that things were entered, or to return things "as they are in the database."  There is no such thing as getting data out "the way it is stored" because a relational database may move or re-order data temporarily to efficiently execute a query depending on indexes available.   You must always explicitly specify how you want your results using an ORDER BY clause.  If you want to keep track of the order that you added data to a table, you should have a "timestamp" column that records the exact moment each row was added via a DEFAULT value or a trigger.  Or, at the very least, you can use an IDENTITY.  Then, you can simply order by that column.  This is a very important concept to understand when working with relational databases.  Things are returned based on the data itself, not based on physical storage characteristics.  I hope this helps.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
In response to &lt;a href="http://weblogs.sqlteam.com/jeffs/archive/2006/07/21/10728.aspx"&gt;Always Use Parameters&lt;/a&gt;,&lt;span style="font-weight: bold;"&gt; Karuna &lt;/span&gt;writes:&lt;br /&gt;
&lt;br /&gt;
&lt;span id="apnlCommentsWrapper$RBS_Holder"&gt;&lt;span id="apnlCommentsWrapper" ajaxcall="async"&gt;&lt;/span&gt;&lt;/span&gt;
&lt;div style="margin-left: 40px; font-style: italic;"&gt;Hi Jeff,&lt;br /&gt;
Just wondering if I build the Sql in Stored Procedure (Dynamic Sql) based on the parameters passed to stored proc, will it still be a possible candidate for Sql Injection? Basically I want to build the Sql in the stored procedure instead of doing it in .Net code as displayed in the article.&lt;br /&gt;
&lt;br /&gt;
Dim cm As New SqlCommand("", YourConnection)&lt;br /&gt;
cm.CommandText = "DELETE FROM YourTable WHERE ID=@ID "&lt;br /&gt;
cm.Parameters.Add("@ID", SqlDbType.Int).Value = ID&lt;br /&gt;
&lt;br /&gt;
If Name &amp;lt;&amp;gt; "" Then&lt;br /&gt;
cm.CommandText &amp;amp;= " And Name=@name"&lt;br /&gt;
cm.Parameters.Add("@Name", SqlDbType.VarChar).Value = Name&lt;br /&gt;
End If&lt;br /&gt;
If TranDate &amp;lt;&amp;gt; DateTime.MinValue Then&lt;br /&gt;
cm.CommandText &amp;amp;= " And TranDate = @TranDate"&lt;br /&gt;
cm.Parameters.Add("@TranDate", SqlDbType.DateTime).Value = TranDate&lt;br /&gt;
&lt;a id="Comments_ascx_CommentList_ctl18_NameLink" title="karuna" target="_blank"&gt;&lt;br /&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;span id="apnlCommentsWrapper$RBS_Holder"&gt;&lt;span id="apnlCommentsWrapper" ajaxcall="async"&gt;
&lt;div class="postfoot"&gt; 				&lt;/div&gt;
&lt;/span&gt;&lt;/span&gt;Hi Karuna -- you are absolutely 100% safe from SQL Injection by doing this.  Remember, SQL Injection is not about genereal SQL concatenation or about building a SQL statement dynamically.  It only can happen when you &lt;span style="font-weight: bold;"&gt;concatenate user input into a SQL string and execute it.  &lt;/span&gt;If you put together a big SQL statement via concatenation but you only incorporate user input via parameters, there's no need for scrubbing data or worrying in any way about SQL Injection -- it will never happen, under any circumstance.  &lt;br /&gt;
&lt;br /&gt;
Avoiding SQL Injection is the easiest thing in the world -- simply do things the easy and correct way and you'll never need to worry about it.   It's like if there was a big controversy in the news about thousands of people crashing their cars because they are driving them with decorative tin foil covering their windshields, and asking the experts "how can we solve this crisis?"   Should we cut holes in the tin foil, or add mirrors, or incorporate a camera and a tv monitor?  Uh .. no.  You should just take the tin foil off of your windshield and do things the easy, simple and correct way and don't make things over complicated.   That's basically what this whole SQL Injection thing is about -- bad programmers doing stupid things when all they need to do is write decent code the &lt;span style="font-weight: bold;"&gt;easy way&lt;/span&gt; -- simply by using parameters.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-weight: bold;"&gt;Gocs &lt;/span&gt;writes:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-style: italic;"&gt;I have tried to compute the number of hours based on the datetime in MS SQL 2005.  However, I am not sure the hours is correct.  Do you have any idea on how to do it correctly?&lt;/span&gt;&lt;br style="font-style: italic;" /&gt;
&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
Gocs -- I think you really need to read &lt;a href="http://weblogs.sqlteam.com/jeffs/archive/2008/05/13/question-needed-not-answer.aspx"&gt;this&lt;/a&gt; very carefully.  I'll be waiting!&lt;img src="http://weblogs.sqlteam.com/jeffs/aggbug/60652.aspx" width="1" height="1" /&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/j29-gBJctzzxeQtf2cXyoFXOQNM/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/j29-gBJctzzxeQtf2cXyoFXOQNM/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/j29-gBJctzzxeQtf2cXyoFXOQNM/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/j29-gBJctzzxeQtf2cXyoFXOQNM/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=SjFLPJCDuQg:Zwh9bk2hb6I:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=SjFLPJCDuQg:Zwh9bk2hb6I:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?i=SjFLPJCDuQg:Zwh9bk2hb6I:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=SjFLPJCDuQg:Zwh9bk2hb6I:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?i=SjFLPJCDuQg:Zwh9bk2hb6I:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=SjFLPJCDuQg:Zwh9bk2hb6I:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?i=SjFLPJCDuQg:Zwh9bk2hb6I:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=SjFLPJCDuQg:Zwh9bk2hb6I:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</description>
            <dc:creator>Jeff Smith</dc:creator>
            <guid>http://weblogs.sqlteam.com/jeffs/archive/2008/07/16/60652.aspx</guid>
            <pubDate>Wed, 16 Jul 2008 20:32:51 GMT</pubDate>
            <wfw:comment>http://weblogs.sqlteam.com/jeffs/comments/60652.aspx</wfw:comment>
            <comments>http://weblogs.sqlteam.com/jeffs/archive/2008/07/16/60652.aspx#feedback</comments>
            <wfw:commentRss>http://weblogs.sqlteam.com/jeffs/comments/commentRss/60652.aspx</wfw:commentRss>
            <trackback:ping>http://weblogs.sqlteam.com/jeffs/services/trackbacks/60652.aspx</trackback:ping>
        </item>
        <item>
            <title>The Golden Rule of Data Manipulation</title>
            <link>http://weblogs.sqlteam.com/jeffs/archive/2008/06/11/golden-rule-of-data-manipulation.aspx</link>
            <description>&lt;span style="font-weight: bold;"&gt;Introduction&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
There is a very simple rule when it comes to storing (and returning) data, which I see violated all the time, making life so much more complicated for everyone involved.  In case you haven't noticed, that's a common theme I discuss here on this blog -- different ways programmers make life more difficult for themselves, instead of simply following good practices and doing things the easy way.  This is yet another example of that situation.&lt;br /&gt;
&lt;br /&gt;
The "Golden Rule of Data Manipulation" is a simple, but important rule that you should always follow when designing a database,  writing database code, or really writing &lt;span style="font-style: italic;"&gt;any &lt;/span&gt;application code at all for that matter:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-style: italic;"&gt;"It is always easier and more flexible to combine data elements rather than to break them apart"&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
In other words: Concatenation is easy. Parsing is hard.  Often, &lt;span style="font-style: italic;"&gt;very&lt;/span&gt; hard -- or even impossible depending on the data.&lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-weight: bold;"&gt;Problems with Parsing&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
It is amazing how often I see people struggling with "difficult SQL problems" such as:&lt;br /&gt;
&lt;ul&gt;
    &lt;li&gt;Working with CSV lists of values in a single column, such as "1,3,56,2"&lt;/li&gt;
    &lt;li&gt;Breaking out a FirstName/MiddleName/LastName/Suffix from a single "Name" column&lt;/li&gt;
    &lt;li&gt;Parsing address strings into City/State/ZIP, or Number/Street/Unit&lt;/li&gt;
    &lt;li&gt;Parsing Phone Numbers to get just an area code, or to take different phone formats and present them all uniformly&lt;/li&gt;
    &lt;li&gt;Figuring out how to calculate the Day,Month, and/or Year from different string values such as "23-Jan-08", "2008-02", "20070303", "03032007"&lt;/li&gt;
&lt;/ul&gt;
And on and on it goes....&lt;br /&gt;
&lt;br /&gt;
Now, sometimes you inherit or import data that needs to be parsed -- that's a fact of life.  You've got to figure out how to do it, and the key in those cases is to accept that because the data itself is essentially random, nothing you can write will perfectly work 100% of the time on all of it.  Often, the best you can do is handle most of the data, and then do some manual clean up.  &lt;br /&gt;
&lt;br /&gt;
Parsing strings can be a very difficult task for any programmer, and the challenge isn't writing the code, it's coming up with the algorithm (another common theme on this blog).  Consider my new favorite example of why parsing a single Name column into a First/Middle/Last is not as easy as it seems:&lt;br /&gt;
&lt;br /&gt;
    &lt;span style="font-style: italic;"&gt;Oscar De La Hoya&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
How would your algorithm parse that one?  Never mind prefixes such as "Dr." and suffixes such as "Jr."!  &lt;br /&gt;
&lt;br /&gt;
Please don't interpret what I am saying as a programming challenge -- I understand that it is possible to write long code with a list of exceptions or rules and have that algorithm work pretty well in most cases.  The point is that writing that algorithm is a lot of work, running it will be inefficient, and it will never be exact because the data itself that is being processed is essentially random.  It's just like the old saying: "garbage in, garbage out".  Still one of my favorites, after all these years, and it still applies!&lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-weight: bold;"&gt;A Data Model that requires Parsing = A Poor Data Model&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
So, we need to accept that sometimes you've got to parse data like this.  And that's OK; it happens, it can be done, even if some manual work is often involved.&lt;br /&gt;
&lt;br /&gt;
However, there's no excuse when you design your database, your SQL code, or applications so that free-form data &lt;span style="font-style: italic;"&gt;must &lt;/span&gt;be parsed, when you can simply design it correctly in the first place and store your data already broken out into the smallest possible units with the correct data types.&lt;br /&gt;
&lt;br /&gt;
If breaking out a contact's name into First, Last, Middle, etc is important to your application, then you should force the point of data entry to accept input broken out into those columns.  The same goes for phone numbers, addresses, and so on.  Any time you have the option of accepting  input as clean, short, raw, segments of data you should always do it.   Once you have data at that smaller resolution, it is trivial to combine it any way that you want for presentation, formatting, filtering, and so on.&lt;br /&gt;
&lt;br /&gt;
It may seem like overkill to break out a phone number into 4 columns:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;AreaCode&lt;br /&gt;
Exchange&lt;br /&gt;
Number&lt;br /&gt;
Extension&lt;br /&gt;
&lt;/div&gt;
    &lt;br /&gt;
And, in fact, it might be more complicated than that if you need to deal with international phone numbers.  You may look at your tables, and your code, and even the UI that accepts these fields and think "that is way too precise and unnecessary, breaking out phone numbers like this sure makes things complicated!"&lt;br /&gt;
&lt;br /&gt;
But by doing this, and only accepting user input that follows precise rules of what is allowed in these fields, and storing each of them in their own column, you can now easily and efficiently:&lt;br /&gt;
&lt;ol&gt;
    &lt;li&gt;Sort these numbers any way you want, without worrying about extra characters like parenthesis or dashes, or leading 1s, messing things up&lt;/li&gt;
    &lt;li&gt;Filter quickly on an area code without the need to use LIKE, and again worrying about extra characters getting in the way&lt;/li&gt;
    &lt;li&gt;Present the phone number quickly and easily any way you want without any parsing, be it as 123.123.1345 x123 or "(123) 123-1345 extension 123", or anything you want. &lt;/li&gt;
    &lt;li&gt;Validate your phone numbers, ensuring you have all the necessary parts and they are the proper length, without worrying about parsing strings&lt;/li&gt;
&lt;/ol&gt;
Considering doing any of those things if your data is stored in random strings like:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;1-123-124-1234&lt;br /&gt;
(123) 124-1234&lt;br /&gt;
123-124-1234, ex. 123&lt;br /&gt;
123.124.1234 x123&lt;br /&gt;
(123)124.1234 ext. 123&lt;br /&gt;
1 123 124 1234 123&lt;br /&gt;
&lt;/div&gt;
    &lt;br /&gt;
and so on ...  Not so easy in that case, just as parsing simple "Name" columns into First/Last, or addresses into  Number/Street/Unit is not so easy as well.  &lt;br /&gt;
&lt;br /&gt;
Again, this is not a programming challenge -- I am sure it can done. (In fact, phone numbers are generally the easiest because you can usually just ignore anything other than digits.)  Most of us have done it before.  But designing something in such a way that parsing is &lt;span style="font-style: italic;"&gt;required&lt;/span&gt; to do simple filtering, sorting, or formatting, is a &lt;span style="font-style: italic;"&gt;bad design&lt;/span&gt;.  &lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-weight: bold;"&gt;It's Not About the UI&lt;br /&gt;
&lt;br /&gt;
&lt;/span&gt;As I wrote &lt;a href="http://weblogs.sqlteam.com/jeffs/archive/2007/06/05/60223.aspx"&gt;here&lt;/a&gt;, you should never think "I want to display phone numbers like 123.123.1234, so I should store them and return them that way."  You should always think "How can I break this down into small, concrete parts that are easily validated and easy to combine any way I want at any time?"&lt;br /&gt;
&lt;br /&gt;
So, what if you need fine detail when storing addresses, but you don't want your UI to present Street Number, Street Name, Unit Type, Unit Number as different data entry fields for usability or aesthetic reasons?  That's fine, but that doesn't mean you should not set up your database properly.  Your UI can certainly still present that one single "Address" text box for the user to fill out, parse that text &lt;span style="font-style: italic;"&gt;at data entry&lt;/span&gt;, show the user the parsed result in multiple fields, and ask "Please verify for your address" or something along those lines.   Then, if not, the user can tweak the results and save it.  If you do things along those lines, and focus on getting the data parsed and stored correctly at the earliest point possible, every other part of your code will be that much more efficient.&lt;br /&gt;
&lt;br /&gt;
All of this applies not only to data storage, but to how data is returned and passed between tiers as well.  Again, if you just return separate columns to your client application, instead of focusing on making them "look nice" in your database code by returning nothing but long, "pre-formatted" strings, your client can simply concatenate and format those columns any way it needs.  And, different clients can format that same database output in different ways -- all without ever altering any database code!  &lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-weight: bold;"&gt;Conclusion&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
In short, remember that writing concatenation is easy, efficient, and exact.  Writing a parsing routine, on the other hand, is often &lt;span style="font-style: italic;"&gt;none &lt;/span&gt;of those things.   You may not always be able to control the design of the data you are working with, but be sure that when you can, you do it right.  If you find yourself using lots of LIKE expressions, or string parsing for simple data retrieval operations, something is wrong.   Time to fix up your database and your code, store the parsed and validated data permanently, and make things easier and cleaner for everyone.&lt;br /&gt;
&lt;br /&gt;
Whether you are designing a schema, writing a SELECT, or writing code in any other programming language, remember that the Golden Rule of Data Manipulation &lt;span style="font-style: italic;"&gt;always &lt;/span&gt;applies.  Accept this rule, learn from it, and practice it, and you might be surprised to find that programming isn't quite as hard as you thought it was.&lt;img src="http://weblogs.sqlteam.com/jeffs/aggbug/60624.aspx" width="1" height="1" /&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/-8lVUWcUkyCI2S-t0qRlHWWdPOA/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/-8lVUWcUkyCI2S-t0qRlHWWdPOA/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/-8lVUWcUkyCI2S-t0qRlHWWdPOA/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/-8lVUWcUkyCI2S-t0qRlHWWdPOA/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=pgTYYBIRmK4:5_YKhwzg6cQ:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=pgTYYBIRmK4:5_YKhwzg6cQ:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?i=pgTYYBIRmK4:5_YKhwzg6cQ:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=pgTYYBIRmK4:5_YKhwzg6cQ:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?i=pgTYYBIRmK4:5_YKhwzg6cQ:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=pgTYYBIRmK4:5_YKhwzg6cQ:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?i=pgTYYBIRmK4:5_YKhwzg6cQ:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=pgTYYBIRmK4:5_YKhwzg6cQ:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</description>
            <dc:creator>Jeff Smith</dc:creator>
            <guid>http://weblogs.sqlteam.com/jeffs/archive/2008/06/11/golden-rule-of-data-manipulation.aspx</guid>
            <pubDate>Wed, 11 Jun 2008 15:48:41 GMT</pubDate>
            <wfw:comment>http://weblogs.sqlteam.com/jeffs/comments/60624.aspx</wfw:comment>
            <comments>http://weblogs.sqlteam.com/jeffs/archive/2008/06/11/golden-rule-of-data-manipulation.aspx#feedback</comments>
            <slash:comments>9</slash:comments>
            <wfw:commentRss>http://weblogs.sqlteam.com/jeffs/comments/commentRss/60624.aspx</wfw:commentRss>
            <trackback:ping>http://weblogs.sqlteam.com/jeffs/services/trackbacks/60624.aspx</trackback:ping>
        </item>
        <item>
            <title>The Joy of Blog Feedback</title>
            <link>http://weblogs.sqlteam.com/jeffs/archive/2008/06/06/the-joy-of-blog-feedback.aspx</link>
            <description>&lt;span style="font-weight: bold;"&gt;Introduction&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
I have been writing my little blog here for some time now, and my favorite part of doing this is of course the feedback.  It's always great to hear from the readers, to have mistakes corrected, to debate various topics and techniques, and to learn a lot about SQL and the various topics I discuss here.  &lt;br /&gt;
&lt;br /&gt;
At this point, I have received over 1,700 comments over the years, and while all of them are truly appreciated, I have noticed that unfortunately many of the, uh, less helpful comments do seem to consistently fall neatly into various categories.  &lt;br /&gt;
&lt;br /&gt;
Let's take a look at an example of a simple, typical blog post and some of the responses that often come back.  If you write a blog of your own, or often read the feedback from other blogs, many of these may seem familiar to you.  &lt;br /&gt;
&lt;span style="font-weight: bold;"&gt;&lt;br /&gt;
A Typical Blog Post&lt;/span&gt;&lt;br style="font-weight: bold;" /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px; font-style: italic;"&gt;Today, I have a simple tip for beginner SQL programmers.  When writing a SELECT, you can add a WHERE clause to filter the results that are returned.&lt;br /&gt;
&lt;br /&gt;
For example, to only return rows for CustomerID 345, you can write:&lt;br /&gt;
&lt;br /&gt;
SELECT ...&lt;br /&gt;
FROM YourTable&lt;br /&gt;
WHERE CustomerID = 345&lt;br /&gt;
&lt;br /&gt;
As you can see, it is very simple. You can use any boolean expression to filter the results as needed. Try it out!  If you have any questions, let me know.&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;span style="font-weight: bold;"&gt;Some Typical Responses&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
The subtle blog spammer (that you initially mistake for a nice complement):&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-style: italic;"&gt;Very helpful site! Good advice!  From Joe at www.some-random-site-that-has-nothing-to-do-with-sql.com&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
The person who doesn't seem to get it:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-style: italic;"&gt;Ummm ... what good does this do when I want to sort? &lt;/span&gt;&lt;span style="font-style: italic;"&gt;You should fix the code.&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
The person that &lt;span style="font-style: italic;"&gt;really &lt;/span&gt;doesn't get it:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-style: italic;"&gt;The problem with that is it will only return results for one customer. &lt;br /&gt;
&lt;br /&gt;
&lt;/span&gt;&lt;/div&gt;
The person that somehow takes away the exact &lt;span style="font-style: italic;"&gt;opposite &lt;/span&gt;of what you wrote:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-style: italic;"&gt;I disagree, this will not return all customers other than 345 and this is definitely not something for advanced SQL Programmers, it's probably better for beginners.&lt;br /&gt;
&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
The script kiddie (who just wants to cut and paste your code, not read or learn anything):&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-style: italic;"&gt;LOL, that doesnt even run 4 me!  I get errorz that sez "YourTable" does not exist!  Plz help!!  thnx!&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
The very clichéd, mindless "anti-Microsoft ranter":&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-style: italic;"&gt;You only have to use WHERE clauses because Bill Gates wants more $$, you are a shill!! Micro$oft sucks, you should use an iPhone for this!  MySQL automagically filters results for you!&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
The "skimmer" (who just skims the post missing most of it):&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-style: italic;"&gt;Nice, but is there any way to filter for just one customer? &lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
The "repeater" (who just repeats what you've already written):&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-style: italic;"&gt;A better solution is to write WHERE CustomerID = 345, it works better.  It is also fast because less rows are returned.  Using WHERE is a good way to filter a SELECT.&lt;/span&gt;&lt;br style="font-style: italic;" /&gt;
&lt;/div&gt;
&lt;br /&gt;
The "know-it-all complainer":&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-style: italic;"&gt;That is the stupidest advice I ever read, why would you want to ever do this? Just use a parameter, or an ORM tool-- this will not scale!  &lt;/span&gt;&lt;span style="font-style: italic;"&gt;I sure hope CustomerID isn't a VARCHAR -- then you have an implicit conversion happening, your indexes are shot, your server will overheat, and your wife will leave you for your mechanic.  Also, 345 is too large if CustomerID is a tinyint.&lt;/span&gt;&lt;br style="font-style: italic;" /&gt;
&lt;/div&gt;
&lt;br /&gt;
The random, unrelated question asker:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-style: italic;"&gt;Good advice. Thnx.  How to insert into the table?&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
The "misunderstander":&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px; font-style: italic;"&gt;If I add this to all of my scripts, only data for one customer will ever be returned.  I am not sure this is a good idea. Also, this code will not work in Java and doesn't follow the HTML 4.0 specification.  &lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
The very rare polite and helpful typo alerter:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-style: italic;"&gt;Hey there, you have a typo in the first sentence -- should be "filter", not "fitler"! Just letting you know, thanks for a great post!&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
The much more common typo alerter:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-style: italic;"&gt;You wrote FITLER not FILTER, your an idiot!! if you cannot write English how can you write SQL ???   Learn to spell!&lt;br /&gt;
&lt;br /&gt;
&lt;/span&gt;&lt;/div&gt;
&lt;span style="font-weight: bold;"&gt;Summary&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
Please, don't misunderstand, I mean this all in good fun.  I love feedback, and please, keep it coming.  It's what makes this and every other blog a fun place to visit. &lt;span style="font-style: italic;"&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;/span&gt;In fact, I realize that I left out the most annoying feedback of all!  That's right, the Thin-Skinned, Overly-Defensive Blog Author Who Feels the Need to Respond to Everything:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-style: italic;"&gt;Did you even read what I wrote? I did not say that.  And, yes, I did spell "monkey" wrong, so sue me!  Remind me to fire my editor.... or maybe I should refund your subscription fee?  Oh, wait, this blog is free!  So what the heck are you complaining about?  Why don't you go bother some MySQL blogger?  I hear they usually write at a 5th grade level which is probably more appropriate for your intellect. Jerk!&lt;br /&gt;
&lt;br /&gt;
&lt;/span&gt;&lt;/div&gt;
Yeah, comments like those are &lt;span style="font-style: italic;"&gt;definitely &lt;/span&gt;the worst of all!  Thank &lt;span style="font-style: italic;"&gt;you &lt;/span&gt;for putting up with &lt;span style="font-style: italic;"&gt;my &lt;/span&gt;feedback, now that I think of it! &lt;span style="font-style: italic;"&gt;&lt;/span&gt;&lt;img src="http://weblogs.sqlteam.com/jeffs/aggbug/60620.aspx" width="1" height="1" /&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/szodC0r1T9lsql8TWNyy9s0k_V8/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/szodC0r1T9lsql8TWNyy9s0k_V8/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/szodC0r1T9lsql8TWNyy9s0k_V8/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/szodC0r1T9lsql8TWNyy9s0k_V8/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=QM9HTr5QD3I:3f4qkGIivok:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=QM9HTr5QD3I:3f4qkGIivok:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?i=QM9HTr5QD3I:3f4qkGIivok:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=QM9HTr5QD3I:3f4qkGIivok:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?i=QM9HTr5QD3I:3f4qkGIivok:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=QM9HTr5QD3I:3f4qkGIivok:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?i=QM9HTr5QD3I:3f4qkGIivok:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=QM9HTr5QD3I:3f4qkGIivok:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</description>
            <dc:creator>Jeff Smith</dc:creator>
            <guid>http://weblogs.sqlteam.com/jeffs/archive/2008/06/06/the-joy-of-blog-feedback.aspx</guid>
            <pubDate>Fri, 06 Jun 2008 19:43:05 GMT</pubDate>
            <wfw:comment>http://weblogs.sqlteam.com/jeffs/comments/60620.aspx</wfw:comment>
            <comments>http://weblogs.sqlteam.com/jeffs/archive/2008/06/06/the-joy-of-blog-feedback.aspx#feedback</comments>
            <slash:comments>11</slash:comments>
            <wfw:commentRss>http://weblogs.sqlteam.com/jeffs/comments/commentRss/60620.aspx</wfw:commentRss>
            <trackback:ping>http://weblogs.sqlteam.com/jeffs/services/trackbacks/60620.aspx</trackback:ping>
        </item>
        <item>
            <title>The Truth about "Cursor Busting" in SQL</title>
            <link>http://weblogs.sqlteam.com/jeffs/archive/2008/06/05/sql-server-cursor-removal.aspx</link>
            <description>Let's say you are called in to troubleshoot a stored procedure that is performing poorly.&lt;br /&gt;
&lt;br /&gt;
You dive in to investigate and this is what you find:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;create procedure ProcessProducts&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;as&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    declare @Products cursor, @ProductID int&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    set @Products = cursor for select ProductID from Products order by ProductID&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    open @Products&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    fetch next from @Products into @ProductID&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    while (@@FETCH_STATUS=0)&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;        begin&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;&lt;span style="font-family: Courier New;"&gt;        exec DoSomething @ProductID&lt;br /&gt;
&lt;/span&gt; &lt;span style="font-family: Courier New;"&gt;        fetch next from @Products into @ProductID&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;        end&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    deallocate @Products&lt;/span&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
Ah ha! A &lt;a href="http://en.wikipedia.org/wiki/Cursor_(databases)" target="_blank"&gt;cursor&lt;/a&gt;!  It seems we have identified the bottleneck: Clearly, the performance problems are because the code is not doing things in a &lt;a href="http://weblogs.sqlteam.com/jeffs/archive/2007/04/30/60192.aspx" target="_blank"&gt;set-based&lt;/a&gt; manner, but rather by processing rows one at a time using a dreaded cursor.  This cursor is opening up the Products table, looping through the rows one at a time, and calling the "DoSomething" stored procedure for each ProductID.  As we all know, cursors are not the way to go when writing SQL code; this cursor should eliminated and replaced with a cleaner, more efficient (and more socially acceptable!) solution.&lt;br /&gt;
&lt;br /&gt;
So, how we do optimize this?  Well, a commonly suggested approach is to eliminate the CURSOR by replacing it with a WHILE loop:&lt;br /&gt;
&lt;br /&gt;
    &lt;span style="font-family: Courier New;"&gt;declare @ProductID int&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    set @ProductID = -99999&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    while (@ProductID is not null)&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;        begin&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;        set @ProductID = (select top 1 ProductID &lt;br /&gt;
                          from Products &lt;br /&gt;
                          where ProductID &amp;gt; @ProductID &lt;br /&gt;
                          order by ProductID asc)&lt;br /&gt;
&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;        exec DoSomething @ProductID&lt;br /&gt;
&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;        end&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
Instead of declaring a CURSOR to loop through the table, we now are using "set-based" code and our problems seem to be solved.  The cursor is gone, our code looks much cleaner, we've tested it and it works properly, so off to production it goes.  Another cursor has been busted!&lt;br /&gt;
&lt;br /&gt;
Right?&lt;br /&gt;
&lt;br /&gt;
Actually ... no. &lt;br /&gt;
&lt;br /&gt;
You see, eliminating cursors is &lt;span style="font-style: italic;"&gt;not about syntax&lt;/span&gt;.  It is not about searching for the word "cursor" in your code and just replacing it with a WHILE loop that does the same thing.  Optimizing and replacing cursors involves much more.  We can never optimize any cursor code until we look deeper into what exactly is happening when we "process" each of those rows.  In this case, we need to find out what that "DoSomething" procedure is actually doing.  &lt;br /&gt;
&lt;br /&gt;
Suppose the DoSomething procedure is generating a report and sending an email to the "Product Manager" for each product that contains status information, and then logging this email message into a table somewhere. &lt;br /&gt;
&lt;br /&gt;
If that is the case, what have we just gained by replacing our CURSOR?   &lt;br /&gt;
&lt;br /&gt;
Honestly -- not much,  if anything at all.  Because of the task at hand, we may very well need to process rows in the Product table one-by-one to send our emails and generate the report, and the bottleneck here is not the cursor code at all, but rather the report generation and maybe sending the email.   Eliminating the cursor code probably gains us nothing here.  If you need to process rows one at a time, go ahead and use a cursor -- that's what they are there for!   Replacing a perfectly fine, simple cursor with a WHILE loop might even make your code longer, or more confusing, or even less efficient depending on circumstances.  &lt;br /&gt;
&lt;br /&gt;
For example, what if we need to process the Products ordered by Region, then Product Name, for whatever reason.  Our cursor code is simple:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;set @Products = cursor for &lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    select ProductID &lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    from Products &lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;&lt;/span&gt;&lt;span style="font-family: Courier New;"&gt;    order by Region, ProductName&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
All that we needed  to change was our ORDER BY clause.  Now, how would we write this as a WHILE loop?  Is it possible?  Sure.  Will it be as simple and clean as using a cursor?  No, it won't. (Though ROW_COUNT() makes this much easier than it used to be)&lt;br /&gt;
&lt;br /&gt;
Now, I am not here to say that cursors are "good", but if you &lt;span style="font-style: italic;"&gt;really need&lt;/span&gt; to process rows one by one, go ahead and proudly use a cursor.   Replacing cursors isn't about processing rows one-by-one in a &lt;span style="font-style: italic;"&gt;different way&lt;/span&gt; (i.e., using a WHILE loop instead), it is about &lt;span style="font-style: italic;"&gt;not &lt;/span&gt;processing rows one-by-one &lt;span style="font-style: italic;"&gt;at all&lt;/span&gt;!    &lt;br /&gt;
&lt;br /&gt;
Let's consider another scenario: What if the DoSomething stored procedure is checking to see if the Product's ExpireDate is greater than today's date, and if so, it is updates the Status column for that Product to 'X'.&lt;br /&gt;
&lt;br /&gt;
In that situation, what have we gained by rewriting ProcessProducts without a cursor, and using a WHILE loop instead?   The answer is, once again: &lt;span style="font-style: italic;"&gt;nothing!&lt;/span&gt;  In fact, we potentially have once again made our code more confusing or even less efficient than a cursor might be!  Remember, the bottleneck isn't the cursor syntax -- it is the fact that we are processing rows one at a time.  Replacing the cursor with the WHILE loop didn't solve this problem, did it?   &lt;br /&gt;
&lt;br /&gt;
So, looking now at both of the scenarios I presented for the DoSomething stored procedure, it should be clear that we did not fix anything by replacing the cursor in either case simply by writing a WHILE loop.  If that's all you are doing, &lt;span style="font-style: italic;"&gt;don't bother replacing the cursor at all.  &lt;/span&gt;You haven't optimized anything.&lt;br /&gt;
&lt;br /&gt;
As I said before, the art of replacing a cursor is not a find-and-replace syntax change operation -- it is a fundamental change in how you process your data.  As in the Product report generation and email example, it may be that we simply &lt;span style="font-style: italic;"&gt;need &lt;/span&gt;to process rows one by one, and thus no further optimization is possible from a SQL point of view.  In situations like updating the Product table, however, we do &lt;span style="font-style: italic;"&gt;not &lt;/span&gt;need to process the rows individually -- we can do everything in one single UPDATE statement.  Thus, in order to determine how to optimize the ProcessProducts stored procedure, we needed to dig deeper into entire process as a whole, which included examining the DoSomething stored procedure and determining the full scope of exactly what this "ProcessProducts" stored procedure is doing.  &lt;br /&gt;
&lt;br /&gt;
So, if "DoSomething" is updating the Products table as specified, we now know that a good replacement for our cursor code doesn't result in a WHILE loop and calling a separate stored procedure over and over at all -- it results a true, set-based solution:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;create procedure ProcessProducts&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;as&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    Update Products set Status='X' where ExpireDate &amp;gt; getdate()&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    &lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;/div&gt;
And &lt;span style="font-style: italic;"&gt;THAT &lt;/span&gt;is how you optimize a cursor! No loops, no calling of another stored procedure for each row in a table, no "find-and-replace" cursor code removal.  We examined the entire &lt;span style="font-style: italic;"&gt;process, &lt;/span&gt;and rewrote the entire process, to get it done quicker and shorter and faster without cursors &lt;span style="font-style: italic;"&gt;or &lt;/span&gt;loops.  &lt;br /&gt;
&lt;br /&gt;
Always remember: Replacing a cursor isn't about rewriting your syntax, it is about redesigning your algorithm.&lt;img src="http://weblogs.sqlteam.com/jeffs/aggbug/60616.aspx" width="1" height="1" /&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/rSONiZ-YKCzTiiABrDb2fesJodE/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/rSONiZ-YKCzTiiABrDb2fesJodE/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/rSONiZ-YKCzTiiABrDb2fesJodE/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/rSONiZ-YKCzTiiABrDb2fesJodE/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=Ip45g-qE7YE:4sGpOqPJhr4:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=Ip45g-qE7YE:4sGpOqPJhr4:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?i=Ip45g-qE7YE:4sGpOqPJhr4:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=Ip45g-qE7YE:4sGpOqPJhr4:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?i=Ip45g-qE7YE:4sGpOqPJhr4:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=Ip45g-qE7YE:4sGpOqPJhr4:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?i=Ip45g-qE7YE:4sGpOqPJhr4:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=Ip45g-qE7YE:4sGpOqPJhr4:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</description>
            <dc:creator>Jeff Smith</dc:creator>
            <guid>http://weblogs.sqlteam.com/jeffs/archive/2008/06/05/sql-server-cursor-removal.aspx</guid>
            <pubDate>Thu, 05 Jun 2008 14:56:02 GMT</pubDate>
            <wfw:comment>http://weblogs.sqlteam.com/jeffs/comments/60616.aspx</wfw:comment>
            <comments>http://weblogs.sqlteam.com/jeffs/archive/2008/06/05/sql-server-cursor-removal.aspx#feedback</comments>
            <slash:comments>12</slash:comments>
            <wfw:commentRss>http://weblogs.sqlteam.com/jeffs/comments/commentRss/60616.aspx</wfw:commentRss>
            <trackback:ping>http://weblogs.sqlteam.com/jeffs/services/trackbacks/60616.aspx</trackback:ping>
        </item>
        <item>
            <title>Log Buffer #98</title>
            <link>http://weblogs.sqlteam.com/jeffs/archive/2008/05/23/log-buffer-98.aspx</link>
            <description>&lt;p __designer:dtid="281474976710660"&gt;Hello and welcome to the 98th edition of &lt;a href="http://www.pythian.com/blogs/about-log-buffer" __designer:dtid="281474976710661"&gt;Log Buffer&lt;/a&gt;. My name is Jeff Smith and I  will hosting this week's exciting episode. If, for some reason, you are not  &lt;em&gt;completely&lt;/em&gt; satisfied with this edition, simply write in and  complain to Dave over at &lt;a href="http://www.pythian.com/" __designer:dtid="281474976710662"&gt;The Pythian Group&lt;/a&gt; and you will receive Log  Buffer #99 &lt;strong __designer:dtid="281474976710663"&gt;absolutely free!&lt;/strong&gt;  Now &lt;em __designer:dtid="281474976710664"&gt;that&lt;/em&gt; is a guarantee you can feel  good about. OK, let's get to work. &lt;/p&gt;
&lt;p __designer:dtid="281474976710665"&gt;I  have only limited exposure to both PostgreSQL and MySQL, but I have often  wondered why MySQL is so popular while it seems that &lt;a href="http://www.postgresonline.com/journal/index.php?/archives/51-guid.html" __designer:dtid="281474976710666"&gt;PostgreSQL has the superior features&lt;/a&gt;.   Over at &lt;a href="http://www.xaprb.com/blog/" __designer:dtid="281474976710667"&gt;Xaprb&lt;/a&gt;, they &lt;a href="http://www.xaprb.com/blog/2008/05/18/why-is-mysql-more-popular-than-postgresql/" __designer:dtid="281474976710668"&gt;attempt to answer that very question&lt;/a&gt;.  Be  sure to read the comments from that post, and check out the &lt;a href="http://reddit.com/r/programming/info/6jv1r/comments/" __designer:dtid="281474976710669"&gt;big discussion from that article over at  reddit&lt;/a&gt; as well.  The theory I like the best?  MySQL is easier to pronounce!   (How do you &lt;a href="http://www.postgresql.org/community/survey.33" __designer:dtid="281474976710670"&gt;pronounce "PostgreSQL"&lt;/a&gt; anyway?)&lt;/p&gt;
&lt;p __designer:dtid="281474976710671"&gt;Speaking of MySQL, &lt;a href="http://www.pythian.com/blogs/author/sheeri" __designer:dtid="281474976710672"&gt;Sheeri Cabral&lt;/a&gt; points out that &lt;a href="http://www.pythian.com/blogs/1016/mysql-website-a-reflection-of-values" __designer:dtid="281474976710673"&gt;MySQL's website certainly doesn't do the  product any favors&lt;/a&gt;, and there's also a good discussion at Xaprb on why &lt;a href="http://www.xaprb.com/blog/2008/05/14/mysql-free-software-but-not-open-source/" __designer:dtid="281474976710674"&gt;MySQL is Free Software but &lt;em __designer:dtid="281474976710675"&gt;not&lt;/em&gt; Open Source&lt;/a&gt;.  If you ever wanted  to add a &lt;a href="http://blogs.mysql.com/peterg/2008/05/19/instructions-for-adding-a-new-unicode-collation/" __designer:dtid="281474976710676"&gt;new Unicode collation to MySQL&lt;/a&gt;, &lt;a href="http://blogs.mysql.com/peterg/"&gt;Alexander Barkov and Peter Gulutzan&lt;/a&gt;  provide all the information you'll need.  &lt;a href="http://www.mysqlperformanceblog.com/" __designer:dtid="281474976710677"&gt;Peter at the MySql Performance Blog&lt;/a&gt; tells  us that &lt;a href="http://www.mysqlperformanceblog.com/2008/05/18/wanted-better-memory-profiling-for-mysql/"&gt;MySQL  lacks a good memory profiling tool&lt;/a&gt;, and based on his feedback, others seem  to agree.  (No, not &lt;a href="http://en.wikipedia.org/wiki/Others_(Lost)" __designer:dtid="281474976710679"&gt;&lt;em __designer:dtid="281474976710680"&gt;those&lt;/em&gt; Others&lt;/a&gt;!)  Speaking of MySQL  feature requests, &lt;a href="http://swanhart.livejournal.com/" __designer:dtid="281474976710681"&gt;Justin Swanhart&lt;/a&gt; asks &lt;a href="http://swanhart.livejournal.com/120936.html" __designer:dtid="281474976710682"&gt;"Why does INFORMATION_SCHEMA fail to show  information about TEMPORARY tables?"&lt;/a&gt;  and also lets us know that his &lt;a href="http://swanhart.livejournal.com/121216.html"&gt;materialized view stored  procedures for MySQL have been OKed for release&lt;/a&gt;. &lt;a href="http://crazytoon.com/" __designer:dtid="281474976710683"&gt;Sunny Walia&lt;/a&gt;  (what a great name -- is it possible to &lt;em __designer:dtid="281474976710684"&gt;not&lt;/em&gt; be a fun person with a name like  that?) tells us how to &lt;a href="http://crazytoon.com/2008/05/21/mysql-how-do-you-install-innotop-to-monitor-innodb-in-real-time/" __designer:dtid="281474976710685"&gt;install innotop to monitor innodb information  in real-time&lt;/a&gt; and wonders &lt;a href="http://crazytoon.com/2008/05/17/mysql-missing-data-on-slave-mysql-replicatio/" __designer:dtid="281474976710686"&gt;"Oh dear MySQL slave, where did you put those  rows?"&lt;/a&gt;  Going back to the MySQL Performance Blog, Vadim warns us of a &lt;a href="http://www.mysqlperformanceblog.com/2008/05/20/too-dangerous-command/" __designer:dtid="281474976710687"&gt;dangerous MySQL command&lt;/a&gt;; be sure to keep  that one locked safely away from the kids.&lt;/p&gt;
&lt;p __designer:dtid="281474976710688"&gt;Regarding a product I actually know a little  about, &lt;a href="http://sqlblog.com/blogs/kalen_delaney/" __designer:dtid="281474976710689"&gt;Kalen Delany&lt;/a&gt; has a nice list of &lt;a href="http://sqlblog.com/blogs/kalen_delaney/archive/2008/05/17/free-troubleshooting-tools.aspx" __designer:dtid="281474976710690"&gt;Free SQL Server Troubleshooting Tools&lt;/a&gt; to  check out.  If you haven't seen it yet, my &lt;a href="http://weblogs.sqlteam.com/mladenp" __designer:dtid="281474976710691"&gt;co-blogger here at SQLTeam Mladen&lt;/a&gt; has an  amazingly popular list of &lt;a href="http://weblogs.sqlteam.com/mladenp/archive/2007/11/20/Free-SQL-Server-tools-that-might-make-your-life-a.aspx" __designer:dtid="281474976710692"&gt;Free SQL Server Tools that might make your  life a little easier&lt;/a&gt; that was published a while back but it is always worth  mentioning.  While you are visiting Mladen's blog, don't miss his latest post on  &lt;a href="http://weblogs.sqlteam.com/mladenp/archive/2008/05/21/SQL-Server-2005-Immediate-Deadlock-notifications.aspx" __designer:dtid="281474976710693"&gt;getting immediate deadlock notifications for  SQL Server 2005&lt;/a&gt;.  Also, be sure to leave him lots of comments telling him  that his blog is great but that he is your &lt;em __designer:dtid="281474976710694"&gt;second&lt;/em&gt; favorite SQL Server blogger --  after &lt;a href="../../../../jeffs/" __designer:dtid="281474976710695"&gt;me&lt;/a&gt;, of course!&lt;/p&gt;
&lt;p __designer:dtid="281474976710696"&gt;Still on the topic of SQL Server, &lt;a href="http://blogs.conchango.com/jamiethomson/" __designer:dtid="281474976710697"&gt;Jamie Thomson&lt;/a&gt; provides us with a tip for  &lt;a href="http://blogs.conchango.com/jamiethomson/archive/2008/05/19/ssis-nugget-ensure-your-root-folder-is-valid.aspx" __designer:dtid="281474976710698"&gt;ensuring that your root folder is valid when  using SSIS&lt;/a&gt;.  &lt;a href="http://sqlblog.com/blogs/denis_gobo/" __designer:dtid="281474976710699"&gt;Denis Gobo&lt;/a&gt; asks: &lt;a href="http://sqlblog.com/blogs/denis_gobo/archive/2008/05/16/6827.aspx" __designer:dtid="281474976710700"&gt;What did you do to master SQL?&lt;/a&gt;    (Interestingly enough, for me it was by &lt;a href="../../../../jeffs/archive/2007/06/13/60232.aspx" __designer:dtid="281474976710701"&gt;learning MS Access first&lt;/a&gt;!)  &lt;a href="http://sqlblogcasts.com/blogs/tonyrogerson/default.aspx" __designer:dtid="281474976710702"&gt;Tony Rogerson&lt;/a&gt; warns us of the performance  implications of using &lt;a href="http://sqlblogcasts.com/blogs/tonyrogerson/archive/2008/05/17/non-recursive-common-table-expressions-performance-sucks-2-row-number-is-executed-number-of-cte-references-x-number-of-rows-from-the-anchor.aspx" __designer:dtid="281474976710703"&gt;Row_Number() in non-recursive CTE's&lt;/a&gt;.  And &lt;a href="http://www.sqlskills.com/blogs/paul/default.aspx"&gt;Paul S. Randal&lt;/a&gt; describes a &lt;a href="http://www.sqlskills.com/blogs/paul/2008/05/22/CHECKDBBugThatPeopleAreHittingMsg8967Level16State216.aspx"&gt;CHECKDB  bug that people are hitting&lt;/a&gt;; thankfully, he says that "you can only hit  this bug if you ALREADY have corruption, that it's quite rare, and that there is  a workaround."&lt;/p&gt;
&lt;p&gt;Everyone enjoys a good analogy, right?  After all, a good  analogy is like an ice cream cone: they both are ... hmmm ... OK, well, that's  not a good analogy at all.  Never mind. Speaking of &lt;em __designer:dtid="281474976710705"&gt;bad&lt;/em&gt; analogies, I bet that unlike &lt;a href="http://blogs.mysql.com/peterg/" __designer:dtid="281474976710706"&gt;Peter  Gulutzan&lt;/a&gt; you never really &lt;a href="http://blogs.mysql.com/peterg/2008/05/19/personal-opinion-half-baked-false-analogy/" __designer:dtid="281474976710707"&gt;thought about the expression "half baked"&lt;/a&gt;  before and how it relates to MySQL features.  Well, now there's your chance!   (Of course, a pessimist would prefer "half &lt;em __designer:dtid="281474976710708"&gt;un-&lt;/em&gt;baked", but that's a discussion for  another time.)&lt;/p&gt;
&lt;p __designer:dtid="281474976710709"&gt;A big topic lately has  been &lt;a href="http://blogs.zdnet.com/security/?p=1150" __designer:dtid="281474976710710"&gt;SQL Injection attacks&lt;/a&gt;.  I always find this  funny because this is the easiest problem to avoid in the history of  programming; as &lt;a href="http://www.codeassembly.com/"&gt;CodeAssembly&lt;/a&gt; tells  us, &lt;a href="http://www.codeassembly.com/Are-you-still-worried-about-sql-injection-?/"&gt;"Never  concatenate user input to your queries, without exceptions."&lt;/a&gt;  That's really  all there is to it -- do that, and you are good to go.  As I've written &lt;a href="../../../../jeffs/archive/2006/07/21/10728.aspx" __designer:dtid="281474976710712"&gt;before&lt;/a&gt;, using parameters is not only  safer, but your code is much shorter and simpler than if you concatenate strings  all day long.  &lt;br /&gt;
&lt;/p&gt;
&lt;p __designer:dtid="281474976710714"&gt;&lt;a href="http://phpimpact.wordpress.com/" __designer:dtid="281474976710715"&gt;Federico Cargnelutti&lt;/a&gt; gives us an  introduction on &lt;a href="http://phpimpact.wordpress.com/2008/05/19/managing-and-applying-database-changes-with-liquibase/" __designer:dtid="281474976710716"&gt;managing and applying database changes with  LiquiBase&lt;/a&gt;, an "open source, DBMS-independent library for tracking, managing  and applying database changes."  I have never used &lt;a href="http://www.liquibase.org/" __designer:dtid="281474976710717"&gt;LiquiBase&lt;/a&gt;, but sounds like something worth  looking into.&lt;/p&gt;
&lt;p __designer:dtid="281474976710718"&gt;While reading &lt;a href="http://people.planetpostgresql.org/mha/index.php?/archives/170-pgCon-day-2.html" __designer:dtid="281474976710719"&gt;Magnus Hagander's PostgreSQL Blog&lt;/a&gt;, I found  out that &lt;a href="http://www.informationweek.com/news/software/database/showArticle.jhtml;jsessionid=2O2MI55IBUUTIQSNDLPCKHSCJUNN2JVN?articleID=207801436&amp;amp;pgno=1" __designer:dtid="281474976710720"&gt;Yahoo claims it has the largest SQL database  in a production environment&lt;/a&gt; -- and they use PostgreSQL.  Impressive!  &lt;a href="http://people.planetpostgresql.org/peter/" __designer:dtid="281474976710721"&gt;Peter Eisentraut&lt;/a&gt; checks in from &lt;a href="http://people.planetpostgresql.org/peter/index.php?/archives/24-PGCon-Day-One.html" __designer:dtid="281474976710722"&gt;PGCon Day One&lt;/a&gt;, which included a  presentation of his on porting &lt;a href="http://www.pgcon.org/2008/schedule/events/62.en.html" __designer:dtid="281474976710723"&gt;Oracle Applications to PostgreSQL&lt;/a&gt;.   For  those of you out there using Max OS X, &lt;a href="http://perldiver.livejournal.com/" __designer:dtid="281474976710724"&gt;Perldiver&lt;/a&gt; has summarized instructions on  building &lt;a href="http://perldiver.livejournal.com/35353.html" __designer:dtid="281474976710725"&gt;PostgreSQL on Mac OS X&lt;/a&gt;.  Going off on a  tangent, they just opened a new Apple store &lt;a href="http://www.informationweek.com/news/personal_tech/ipod/showArticle.jhtml?articleID=207800642" __designer:dtid="281474976710726"&gt;here in Boston on Boylston street&lt;/a&gt;.  I  visited it this weekend after getting my usual bad haircut next door.  My  verdict on the store?  It sure looks nice, but I had no luck finding a new 5  1/4" floppy drive for my &lt;a href="http://en.wikipedia.org/wiki/Apple_II" __designer:dtid="281474976710727"&gt;Apple II&lt;/a&gt;.  Try to do better next time,  Apple!&lt;/p&gt;
&lt;p __designer:dtid="281474976710728"&gt;&lt;a href="http://duckdown.blogspot.com/" __designer:dtid="281474976710729"&gt;James  McGovern&lt;/a&gt; &lt;a href="http://duckdown.blogspot.com/2008/05/praise-for-mark-wilcox-of-oracle.html" __designer:dtid="281474976710730"&gt;offers some praise for Mark Wilcox of  Oracle&lt;/a&gt;.  Why? Because Mark has been doing some &lt;a href="http://blogs.oracle.com/mwilcox/" __designer:dtid="281474976710731"&gt;must-read blogging over at the Oracle.com  blogs&lt;/a&gt;.  Getting back to my favorite topic, which is coding SQL, &lt;a href="http://learndiscoverer.blogspot.com/" __designer:dtid="281474976710732"&gt;Michael Armstrong-Smith&lt;/a&gt; instructs the  Oracle crowd on using &lt;a href="http://learndiscoverer.blogspot.com/2008/05/using-case-to-solver-outer-join-issues.html" __designer:dtid="281474976710733"&gt;CASE to solver Outer Join issues&lt;/a&gt;. &lt;a href="http://blogs.oracle.com/shay/" __designer:dtid="281474976710734"&gt;Shay  Shmeltzer&lt;/a&gt; provides some tips on &lt;a href="http://blogs.oracle.com/shay/2008/05/16#a684" __designer:dtid="281474976710735"&gt;creating a master with two details on the same  page&lt;/a&gt; when using ADF.  &lt;/p&gt;
&lt;p __designer:dtid="281474976710736"&gt;Sticking with  Oracle links, &lt;a href="http://www.petefinnigan.com/" __designer:dtid="281474976710737"&gt;Pete Finnigan&lt;/a&gt; ponders &lt;a href="http://www.petefinnigan.com/weblog/archives/00001174.htm" __designer:dtid="281474976710738"&gt;read only tables or read only users&lt;/a&gt;, and  notes that in Oracle a read-only user "has approximately 27,000 other privileges  because of grants to PUBLIC. This is the &lt;strong __designer:dtid="281474976710739"&gt;killer&lt;/strong&gt; issue as because of this it is  in fact not possible to create a read-only user." Hmm ... only 27,000?  Come  on, that doesn't seem that bad to me!  &lt;a href="http://awads.net/wp/" __designer:dtid="281474976710740"&gt;Eddie Awad&lt;/a&gt; tells us about the &lt;a href="http://awads.net/wp/2008/05/20/the-lazy-developer%e2%80%99s-way-to-populate-a-surrogate-key/" __designer:dtid="281474976710741"&gt;Lazy Developer's way to populate a Surrogate  Key&lt;/a&gt; and over at the &lt;a href="http://jonathanlewis.wordpress.com/" __designer:dtid="281474976710742"&gt;Oracle Scratchpad&lt;/a&gt;, &lt;a href="http://www.jlcomp.demon.co.uk/" __designer:dtid="281474976710743"&gt;Jonathan  Lewis&lt;/a&gt; provides &lt;a href="http://jonathanlewis.wordpress.com/2008/05/14/index-efficiency/" __designer:dtid="281474976710744"&gt;some helpful links on Index Efficiency&lt;/a&gt;.   Finally, if you are looking to install Oracle Database 11g Release 1 on Fedora 9  (and who isn't?), &lt;a href="http://www.oracle-base.com/articles/11g/OracleDB11gR1InstallationOnFedora9.php" __designer:dtid="281474976710745"&gt;everything you need to know is covered over at  Oracle-Base&lt;/a&gt;. &lt;/p&gt;
&lt;p __designer:dtid="281474976710746"&gt;Now, if you're like  me, you hate DBAs.  Ah, just kidding, of course we all love our Database  Administration Overlords (and I'm not just saying that because most of the  people reading this probably &lt;em __designer:dtid="281474976710747"&gt;are&lt;/em&gt;  DBAs.)  However, even the best DBAs out there occasionally make mistakes.  If  you have some horror stories of your own to share, or if you simply want to take  pleasure in the misfortune of others, be sure to check out &lt;a href="http://sqlblog.com/blogs/kalen_delaney/archive/2008/05/15/dba-blunders.aspx" __designer:dtid="281474976710748"&gt;Kalen Delany's call for DBA Blunders&lt;/a&gt;.  (Of  course, to be fair, even us &lt;a href="http://sqlblog.com/blogs/denis_gobo/archive/2008/05/21/6913.aspx" __designer:dtid="281474976710749"&gt;developers occasionally make mistakes&lt;/a&gt;.) To  help avoid future blunders, consider this advice: &lt;a href="http://sqlserver-qa.net/blogs/tools/archive/2008/05/19/SQL-Server-Service-Pack-or-Hotfix-patch-or-Cumulative-Update_3A00_-do-not-attempt-Windows-System-Restore-as-a-backout-plan_3F00_.aspx" __designer:dtid="281474976710750"&gt;Do not use Windows System Restore as a backout  plan for SQL Server Service Packs, Cumulative Updates, or HotFixes&lt;/a&gt;.  Also,  &lt;a href="http://weblogs.sqlteam.com/tarad" __designer:dtid="281474976710751"&gt;Tara here at SQLTeam&lt;/a&gt; reminds us to &lt;a href="http://weblogs.sqlteam.com/tarad/archive/2008/05/20/60604.aspx" __designer:dtid="281474976710752"&gt;optimize your tempdb&lt;/a&gt; and even provides a  helpful script.  I'd like to add my own helpful tip for DBAs:  Schedule regular  database backups!   Remember, you read it here first.&lt;/p&gt;
&lt;p __designer:dtid="281474976710753"&gt;Previewing the upcoming &lt;a href="http://www.microsoft.com/sqlserver/2008/en/us/default.aspx" __designer:dtid="281474976710754"&gt;SQL Server 2008&lt;/a&gt;, &lt;a href="http://weblogs.sqlteam.com/derekc/" __designer:dtid="281474976710755"&gt;SQLTeam's Derek&lt;/a&gt; discusses the &lt;a href="http://weblogs.sqlteam.com/derekc/archive/2008/05/20/60603.aspx" __designer:dtid="281474976710756"&gt;Data Profiling Utility with SQL Server  2008&lt;/a&gt;.  It sure seems like a nice tool, but that still may not make people &lt;a href="http://sqlblog.com/blogs/aaron_bertrand/archive/2008/05/20/are-you-or-your-team-nervous-about-sql-server-2008.aspx" __designer:dtid="281474976710757"&gt;any less nervous about SQL 2008&lt;/a&gt;.  (Heck,  my team is still nervous about SQL Server &lt;em __designer:dtid="281474976710758"&gt;2005&lt;/em&gt;!)   &lt;a href="http://sqlblog.com/blogs/aaron_bertrand/" __designer:dtid="281474976710759"&gt;Aaron Betrand&lt;/a&gt; urges people to &lt;a href="http://sqlblog.com/blogs/aaron_bertrand/archive/2008/05/19/do-you-want-intellisense-to-support-sql-server-2005.aspx" __designer:dtid="281474976710760"&gt;vote if you want IntelliSense in SSMS 2008 to  also support SQL Server 2005&lt;/a&gt;, which seems like a great idea to me, and &lt;a href="http://sqlblog.com/blogs/linchi_shea/" __designer:dtid="281474976710761"&gt;Linchi Shea&lt;/a&gt; has a quick analysis of &lt;a href="http://sqlblog.com/blogs/linchi_shea/archive/2008/05/16/sql-server-2008-page-compression-performance-impact-on-table-scans.aspx" __designer:dtid="281474976710762"&gt;SQL Server 2008 Page Compression and its  performance impact on table scans&lt;/a&gt;.  Finally, &lt;a href="http://blogs.conchango.com/jamiethomson/" __designer:dtid="281474976710763"&gt;Jamie Thomson&lt;/a&gt; dissects the &lt;a href="http://blogs.conchango.com/jamiethomson/archive/2008/05/21/fuzzyness.aspx" __designer:dtid="281474976710764"&gt;fuzzyness of SQL Server 2008&lt;/a&gt;.   To me,  "fuzziness" is what happens to my vision after drinking too many &lt;a href="http://en.wikipedia.org/wiki/Mojito" __designer:dtid="281474976710765"&gt;mojitos&lt;/a&gt;, but Jamie is discussing a new  feature in SSIS 2008 so give it a read.&lt;/p&gt;
&lt;p __designer:dtid="281474976710766"&gt;I've always believed that you don't truly know  all there is to know about databases until you understand the raw data  structures of tables and indexes and so on.  Over at &lt;a href="http://channel9.msdn.com/" __designer:dtid="281474976710767"&gt;MSDN Channel  9&lt;/a&gt;, there's a new series of videos on &lt;a href="http://channel9.msdn.com/Showpost.aspx?postid=405619" __designer:dtid="281474976710768"&gt;Data Structures and Algorithms&lt;/a&gt;, so be sure  to watch if you want to know how database engines &lt;em __designer:dtid="281474976710769"&gt;really&lt;/em&gt; work "under the hood".  My  enjoyment of the video was unfortunately interrupted by horrible flashbacks from  my &lt;a href="http://www.cs.umb.edu/www/academic/courses/CS310/"&gt;CS310&lt;/a&gt; days.&lt;/p&gt;
&lt;p __designer:dtid="281474976710770"&gt;Moving away from &lt;em __designer:dtid="281474976710771"&gt;relational&lt;/em&gt; databases, &lt;a href="http://java.dzone.com/users/jimbojw" __designer:dtid="281474976710772"&gt;Jim  Wilson&lt;/a&gt; helps us to &lt;a href="http://java.dzone.com/news/understanding-hbase-and-bigtab" __designer:dtid="281474976710773"&gt;understand HBase and BigTable&lt;/a&gt;.   Apparently, &lt;a href="http://wiki.apache.org/hadoop/Hbase/HbaseArchitecture" __designer:dtid="281474976710774"&gt;HBase&lt;/a&gt; is the open source implementation of  Google's &lt;a href="http://labs.google.com/papers/bigtable.html" __designer:dtid="281474976710775"&gt;BigTable&lt;/a&gt; database, which is described as a  "sparse, distributed, persistent multidimensional sorted map."  In layman's  terms, that means "a database with wicked huge tables."&lt;/p&gt;
&lt;p __designer:dtid="281474976710777"&gt;For those looking for a laugh, be sure to read  &lt;a href="http://sqlblogcasts.com/blogs/acalvett/" __designer:dtid="281474976710778"&gt;Andrew Calvett&lt;/a&gt;'s &lt;a href="http://sqlblogcasts.com/blogs/acalvett/archive/2008/05/21/ms-sql-server-book-of-wisdom.aspx" __designer:dtid="281474976710779"&gt;MS SQL Server Book of Wisdom&lt;/a&gt;.  It reminds  me quite a bit of my infamous and widely misinterpreted &lt;a href="../../../../jeffs/archive/2005/05/24/5248.aspx" __designer:dtid="281474976710780"&gt;Top 10 Things I Hate About SQL Server&lt;/a&gt; post  from way back in the olden days.  Be careful, Andrew: sometimes folks don't get  it if your jokes are too subtle!  (Of course, in my case, it could be that my  jokes just weren't that funny.)&lt;/p&gt;
&lt;p __designer:dtid="281474976710781"&gt;Well,  that's all for this week.  Thanks, Dave, for giving me an opportunity to write  this week's Log Buffer.  It was lot of fun and a welcome opportunity for me to  spend more time than I usually do reading lots of great blog posts from around  the internet.  Have a great weekend everyone!&lt;/p&gt;&lt;img src="http://weblogs.sqlteam.com/jeffs/aggbug/60607.aspx" width="1" height="1" /&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/vqMyKQV28DdrihcY0p15RqzPWvM/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/vqMyKQV28DdrihcY0p15RqzPWvM/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/vqMyKQV28DdrihcY0p15RqzPWvM/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/vqMyKQV28DdrihcY0p15RqzPWvM/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=a9QUn_pHGeE:Z4sC8ymtl8Y:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=a9QUn_pHGeE:Z4sC8ymtl8Y:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?i=a9QUn_pHGeE:Z4sC8ymtl8Y:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=a9QUn_pHGeE:Z4sC8ymtl8Y:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?i=a9QUn_pHGeE:Z4sC8ymtl8Y:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=a9QUn_pHGeE:Z4sC8ymtl8Y:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?i=a9QUn_pHGeE:Z4sC8ymtl8Y:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=a9QUn_pHGeE:Z4sC8ymtl8Y:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</description>
            <dc:creator>Jeff Smith</dc:creator>
            <guid>http://weblogs.sqlteam.com/jeffs/archive/2008/05/23/log-buffer-98.aspx</guid>
            <pubDate>Fri, 23 May 2008 13:09:46 GMT</pubDate>
            <wfw:comment>http://weblogs.sqlteam.com/jeffs/comments/60607.aspx</wfw:comment>
            <comments>http://weblogs.sqlteam.com/jeffs/archive/2008/05/23/log-buffer-98.aspx#feedback</comments>
            <slash:comments>10</slash:comments>
            <wfw:commentRss>http://weblogs.sqlteam.com/jeffs/comments/commentRss/60607.aspx</wfw:commentRss>
            <trackback:ping>http://weblogs.sqlteam.com/jeffs/services/trackbacks/60607.aspx</trackback:ping>
        </item>
        <item>
            <title>Implementing "Interfaces" in SQL</title>
            <link>http://weblogs.sqlteam.com/jeffs/archive/2008/05/19/60601.aspx</link>
            <description>My latest article has just been published over at &lt;a href="http://www.sqlteam.com" target="_blank"&gt;SQLTeam&lt;/a&gt;:&lt;br /&gt;
&lt;br /&gt;
    &lt;a href="http://www.sqlteam.com/article/implementing-table-interfaces" target="_blank" style="font-weight: bold;"&gt;Implementing Table Interfaces&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
When I wrote a &lt;a href="http://www.sqlteam.com/article/implementing-table-inheritance-in-sql-server" target="_blank"&gt;Table Inheritance&lt;/a&gt; article a few months back, the technique shown was pretty standard and straight-forward.  As I was writing it, I thought it would be an interesting challenge to figure out a way to implement table &lt;span style="font-style: italic;"&gt;interfaces&lt;/span&gt; as well, where different tables don't inherit from the same base class, but they still "implement" the same relations.  That definitely was not as easy, and the end result isn't as clean and direct, but I hope this at least provides some ideas and at the very least it should provoke interesting comments and alternative approaches.&lt;img src="http://weblogs.sqlteam.com/jeffs/aggbug/60601.aspx" width="1" height="1" /&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/fQ06k5ULPBAaLdNUtno4bkVVAHE/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/fQ06k5ULPBAaLdNUtno4bkVVAHE/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/fQ06k5ULPBAaLdNUtno4bkVVAHE/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/fQ06k5ULPBAaLdNUtno4bkVVAHE/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=VgAZW6yY2dU:_PxpiBZ2R98:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=VgAZW6yY2dU:_PxpiBZ2R98:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?i=VgAZW6yY2dU:_PxpiBZ2R98:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=VgAZW6yY2dU:_PxpiBZ2R98:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?i=VgAZW6yY2dU:_PxpiBZ2R98:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=VgAZW6yY2dU:_PxpiBZ2R98:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?i=VgAZW6yY2dU:_PxpiBZ2R98:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=VgAZW6yY2dU:_PxpiBZ2R98:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</description>
            <dc:creator>Jeff Smith</dc:creator>
            <guid>http://weblogs.sqlteam.com/jeffs/archive/2008/05/19/60601.aspx</guid>
            <pubDate>Mon, 19 May 2008 13:06:04 GMT</pubDate>
            <wfw:comment>http://weblogs.sqlteam.com/jeffs/comments/60601.aspx</wfw:comment>
            <comments>http://weblogs.sqlteam.com/jeffs/archive/2008/05/19/60601.aspx#feedback</comments>
            <wfw:commentRss>http://weblogs.sqlteam.com/jeffs/comments/commentRss/60601.aspx</wfw:commentRss>
            <trackback:ping>http://weblogs.sqlteam.com/jeffs/services/trackbacks/60601.aspx</trackback:ping>
        </item>
        <item>
            <title>Need an Answer?  Actually, No ... You Need a Question</title>
            <link>http://weblogs.sqlteam.com/jeffs/archive/2008/05/13/question-needed-not-answer.aspx</link>
            <description>Welcome!&lt;br /&gt;
&lt;br /&gt;
The reason you were directed here is because you need assistance, and I am here to help.  I am not, however, here to provide you with any answers!  You see, it looks like the assistance you need is not finding an &lt;span style="font-style: italic;"&gt;answer&lt;/span&gt;; it is rather that you need assistance finding a &lt;span style="font-style: italic;"&gt;question&lt;/span&gt;.&lt;br /&gt;
&lt;br /&gt;
As you know, there are all kinds of questions.  Questions that test memory recall.  Questions that test logic skills.  Brain-teasers and mathematical questions and so on.  But there is one requirement that all good questions must have in common before they can be answered:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-style: italic;"&gt;A proper question MUST provide ALL of the information necessary in order for an answer to be given.  &lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
In other words, if you omit important information from a question, it doesn't matter how simple or easy that question is:  It suddenly becomes very difficult, or even impossible, to answer.  &lt;br /&gt;
&lt;br /&gt;
For example, consider the following question:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-style: italic;"&gt;"Am I wearing a hat?"&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
Seems pretty easy, right? No logic, no memorization, no trivia, no knowledge of any specific topic is required.  &lt;br /&gt;
&lt;br /&gt;
So ... what's the answer?  Take a few minutes, think about it, write it down on a piece of scrap paper.  I can wait, take your time ...&lt;br /&gt;
&lt;br /&gt;
What's that, you say? You &lt;span style="font-style: italic;"&gt;can't &lt;/span&gt;answer that simple question!?  Why not?  I stated it very clearly, it requires a simple YES or NO response, there's nothing tricky there.  So, why would anyone have any trouble giving an answer to something so basic and simple?&lt;br /&gt;
&lt;br /&gt;
The reason, of course, is because you can't see me.  You have no way of knowing what I am wearing because I did not provide you enough information!  As simple as it is, it cannot be answered; therefore, it is &lt;span style="font-style: italic;"&gt;not &lt;/span&gt;a proper question!&lt;br /&gt;
&lt;br /&gt;
Suppose, instead, I provided a picture of myself and asked &lt;span style="font-style: italic;"&gt;"In this picture, am I wearing a hat?"&lt;/span&gt;  And, in the picture, my head is clearly visible and the fact that I am wearing a Boston Red Sox cap is very clear.  Would you be able to answer the question in that scenario?  Of course!  Suddenly, what was an impossible question to answer became very simple!  &lt;br /&gt;
&lt;br /&gt;
How did that happen?&lt;br /&gt;
&lt;br /&gt;
It happened, of course, because I provided you enough &lt;span style="font-style: italic;"&gt;information &lt;/span&gt;to answer the question!  And that is the often problem with many of the questions we see day to day in forums asking for help.  You cannot expect an &lt;span style="font-style: italic;"&gt;answer &lt;/span&gt;unless you provide a proper &lt;span style="font-style: italic;"&gt;question &lt;/span&gt;with all the necessary &lt;span style="font-style: italic;"&gt;information&lt;/span&gt;.  The majority of the effort by those helping others in these forums is not spent answering questions, it is spent trying to figure out what the heck the question actually is!  And that is the problem; people don't seem to realize that they can't just randomly cut and paste code or ask vague questions without any context and expect to receive help!  &lt;br /&gt;
&lt;br /&gt;
Yet, requesting more information and details doesn't always go over so well ... Those looking for help seem to often have trouble understanding why the helpers need more info, why are they are so "anal" and "demanding" about minor things like database schemas, or sample data, or code samples.  "How is that important?" they wonder. "Just answer the question and provide me with some help, please!  An expert would know the answer!"&lt;br /&gt;
&lt;br /&gt;
My goal today is to hopefully help you to understand why information and context is so important for even simple, basic, questions, and how providing that information suddenly transforms a poor question into a very good one that can be quickly and accurately answered.  &lt;br /&gt;
&lt;br /&gt;
Let's try another example:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-style: italic;"&gt;"How do I get from work to Fenway Park?"&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
So, what do you think?  Is that a good question that provides all of the information?  It seems simple, right? It is just asking for basic directions. Yet, where is "work"?  Is it asking for driving directions, or walking directions, or maybe which subway lines to take?  Who knows!!? &lt;br /&gt;
&lt;br /&gt;
As stated, this question simply cannot be answered!   Yet, if I just  thought about it a little and made sure to provide &lt;span style="font-style: italic;"&gt;all &lt;/span&gt;of the necessary information, this "impossible" question with no answer suddenly becomes a very simple one:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-style: italic;"&gt;"How I do get from 125 High Street, Boston, MA to Fenway Park via the subway?"&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
See the difference? Instead of just assuming that everyone knows where I work and what mode of transportation I am looking for, if I make sure to simply &lt;span style="font-style: italic;"&gt;tell them, &lt;/span&gt;there is no uncertainty, no confusion, no guesswork, and the question can be answered.  This isn't rocket science, right?  Yet, these common-sense basics seem to elude many, many, people!&lt;br /&gt;
&lt;br /&gt;
Would you drop your car off at a mechanic with a note on it that says:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-style: italic;"&gt;"Car doesn't work.  Plz fix.  It is urgent! Thx!!"&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
I sure hope not.  You'd explain what's wrong, right?  It has trouble starting, it has a flat tire, there's smoke coming from beneath the hood, the steering wheel fell off, and so on. It's basic common sense that you would do everything you can to be sure that the mechanic has the information he needs to fix your car correctly and promptly, right?  Shouldn't that same logic also apply when asking for help in forums?&lt;br /&gt;
&lt;br /&gt;
Finally, let's try a SQL Server question:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-style: italic;"&gt;"What is wrong with teh codez? it does not work!  Plz Help!&lt;/span&gt;&lt;br style="font-style: italic;" /&gt;
&lt;br style="font-style: italic;" /&gt;
&lt;span style="font-style: italic;"&gt;select SaleID, Customer, Qty, Price&lt;/span&gt;&lt;br style="font-style: italic;" /&gt;
&lt;span style="font-style: italic;"&gt;from SalesNumbesr&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-style: italic;"&gt;Thnx!"&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
Take a look at that question.  It is a very simple SQL statement, right?  There's nothing there that a beginner could not understand.   Can you "spot" the problem and fix it?  &lt;br /&gt;
&lt;br /&gt;
Hmmm ... maybe you can, maybe you can't.  You can't really be sure, can you?  At this point, we can all try to guess what the problem is.  What does "it does not work" even mean? Is "SalesNumbesr" a typo?  Should it be "SalesNumbers" ?  Is it returning too much data?  not enough data?  Incorrect data?  Is it generating an error?  And so on.  &lt;br /&gt;
&lt;br /&gt;
We could spend all day trying to guess what the question is and provide answers to those guesses, but if the guesses are wrong, the answers won't be so helpful, will they?  On the previous question, what if you guessed that I work in Cleveland and provided directions for me to Fenway Park from there?  Would that be helpful to me? Probably not, right?  Most likely, it just wasted everyone's time.&lt;br /&gt;
&lt;br /&gt;
So, getting back to the code .... what is wrong with it?  Well, in SQL terms, the answer is: &lt;span style="font-style: italic;"&gt;NULL&lt;/span&gt;!  It does not exist.  Until we are provided with more information, the question cannot be answered. Thus, it is not a question at all, just an incomplete fragment.  As simple as the question looks, as basic as the T-SQL is, this "question" will stump even the greatest "experts" out there because an answer to this question simply does not exist.&lt;br /&gt;
&lt;br /&gt;
If more information is provided, like this:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-style: italic;"&gt;"Hi -- I currently have the code below:&lt;/span&gt;&lt;br style="font-style: italic;" /&gt;
&lt;br style="font-style: italic;" /&gt;
&lt;span style="font-style: italic;"&gt;select SaleID, Customer, Qty, Price&lt;/span&gt;&lt;br style="font-style: italic;" /&gt;
&lt;span style="font-style: italic;"&gt;from SalesNumbesr&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-style: italic;"&gt;I would also like to return the total Amount for each Sale, which is the Qty multiplied by the Price.  However, I am not sure how to add this to my current code.  Can anyone please help?"&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
Suddenly, the question now is very clear and the answer is very simple!  They just want to know how to add an expression to the result set. Just by providing a little more information, and not assuming that everyone knows what is happening outside of the context of what was written, something that was impossible to answer has become very easy.  &lt;br /&gt;
&lt;br /&gt;
I sure hope this is making sense.&lt;br /&gt;
&lt;br /&gt;
In fact, taking this whole article to its logical conclusion, I think we can safely say:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-style: italic;"&gt;"The more accurate and detailed information a question provides, the more accurate and detailed the answers will be."&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
In other words, a vague, incomplete question can only get, at best, vague, incomplete answers.  But a question that spells out the entire situation very clearly will get, at best, a very clear and specific answer that works in that situation.  &lt;br /&gt;
&lt;br /&gt;
So, please, think of this when you ask questions in a forum.  Consider the fact that no one knows your specific environment, or code, or application, or database except for YOU.  And, no one can help you unless you are providing enough information for them to do so.  &lt;br /&gt;
&lt;br /&gt;
Just like the mechanic. Or someone giving driving directions.  Or a doctor when you are sick.   You provide them with the necessary information so they can help you, right?  Consider doing the same to those providing you with (free!) programming advice.&lt;br /&gt;
&lt;br /&gt;
. . .&lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-style: italic;"&gt;(Feel free to provide this link to those who seen to have trouble understanding that you cannot read their mind when assisting them with programming help on forums.)&lt;br /&gt;
&lt;/span&gt;&lt;img src="http://weblogs.sqlteam.com/jeffs/aggbug/60596.aspx" width="1" height="1" /&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/4PWDQPNpCKAhQ2cKQk2o1-P4KmE/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/4PWDQPNpCKAhQ2cKQk2o1-P4KmE/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/4PWDQPNpCKAhQ2cKQk2o1-P4KmE/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/4PWDQPNpCKAhQ2cKQk2o1-P4KmE/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=0fV0E-njOoo:aXvft-RQ8h8:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=0fV0E-njOoo:aXvft-RQ8h8:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?i=0fV0E-njOoo:aXvft-RQ8h8:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=0fV0E-njOoo:aXvft-RQ8h8:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?i=0fV0E-njOoo:aXvft-RQ8h8:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=0fV0E-njOoo:aXvft-RQ8h8:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?i=0fV0E-njOoo:aXvft-RQ8h8:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=0fV0E-njOoo:aXvft-RQ8h8:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</description>
            <dc:creator>Jeff Smith</dc:creator>
            <guid>http://weblogs.sqlteam.com/jeffs/archive/2008/05/13/question-needed-not-answer.aspx</guid>
            <pubDate>Tue, 13 May 2008 15:04:33 GMT</pubDate>
            <wfw:comment>http://weblogs.sqlteam.com/jeffs/comments/60596.aspx</wfw:comment>
            <comments>http://weblogs.sqlteam.com/jeffs/archive/2008/05/13/question-needed-not-answer.aspx#feedback</comments>
            <slash:comments>19</slash:comments>
            <wfw:commentRss>http://weblogs.sqlteam.com/jeffs/comments/commentRss/60596.aspx</wfw:commentRss>
            <trackback:ping>http://weblogs.sqlteam.com/jeffs/services/trackbacks/60596.aspx</trackback:ping>
        </item>
        <item>
            <title>GROUP BY ALL</title>
            <link>http://weblogs.sqlteam.com/jeffs/archive/2008/05/05/group-by-all.aspx</link>
            <description>Here's an obscure piece of SQL you may not be aware of:  The "ALL" option when using a GROUP BY.&lt;br /&gt;
&lt;br /&gt;
Consider the following table:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;Create table Sales&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;(&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    SaleID int identity not null primary key,&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    CustomerID int,&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    ProductID int,&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    SaleDate datetime,&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    Qty int,&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    Amount money&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;)&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;insert into Sales (CustomerID, ProductID, SaleDate, Qty, Amount)&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;select 1,1,'2008-01-01',12,400 union all&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;select 1,2,'2008-02-25',6,2300 union all&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;select 1,1,'2008-03-02',23,610 union all&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;select 2,4,'2008-01-04',1,75 union all&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;select 2,2,'2008-02-18',52,5200 union all&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;select 3,2,'2008-03-09',99,2300 union all&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;select 3,1,'2008-04-19',3,4890 union all&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;select 3,1,'2008-04-21',74,2840&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;SaleID      CustomerID  ProductID   SaleDate                Qty         Amount&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;----------- ----------- ----------- ----------------------- ----------- ---------------------&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;9           1           1           2008-01-01 00:00:00.000 12          400.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;10          1           2           2008-02-25 00:00:00.000 6           2300.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;11          1           1           2008-03-02 00:00:00.000 23          610.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;12          2           4           2008-01-04 00:00:00.000 1           75.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;13          2           2           2008-02-18 00:00:00.000 52          5200.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;14          3           2           2008-03-09 00:00:00.000 99          2300.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;15          3           1           2008-04-19 00:00:00.000 3           4890.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;16          3           1           2008-04-21 00:00:00.000 74          2840.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;(8 row(s) affected)&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
Suppose we'd like to see the customers that were sold Product #1 along with the total amount that they spent.&lt;br /&gt;
&lt;br /&gt;
We would basically write a simple SELECT with a GROUP BY like this:&lt;br /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;select CustomerID, sum(Amount) as TotalAmount&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;from Sales&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;where ProductID = 1&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;group by CustomerID&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
And sure enough, we'd get our answer:&lt;br /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;CustomerID  TotalAmount&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;----------- ---------------------&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;1           1010.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;3           7730.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;(2 row(s) affected)&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
Now, let's say that we'd like to see &lt;span style="font-style: italic;"&gt;all &lt;/span&gt;customers that have been sold &lt;span style="font-style: italic;"&gt;any &lt;/span&gt;products, but we still just want to see the "TotalAmount" for ProductID #1.  For customers that have never ordered ProductID #1, it should output a "TotalAmount" value of $0.   One way to do this is with a CASE expression; instead of filtering so that only ProductID #1 is returned, we can conditionally SUM() the Amount only for orders for ProductID #1.  Like this:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;select CustomerID, &lt;span style="font-weight: bold;"&gt;sum(case when ProductID=1 then Amount else 0 end) as TotalAmount&lt;/span&gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;from Sales&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;group by CustomerID&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;CustomerID  TotalAmount&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;----------- ---------------------&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;1           1010.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2           0.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;3           7730.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;(3 row(s) affected)&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
That gives us the results we want.   Because we are not using a WHERE clause to filter the data, we see an entry for CustomerID #2 in the output.  &lt;br /&gt;
&lt;br /&gt;
However, in situations where you have written the above SQL, you could actually replace the SUM(CASE...) expression by using &lt;span style="font-weight: bold;"&gt;GROUP BY ALL&lt;/span&gt;, instead of just a standard GROUP BY, like this:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;select CustomerID, sum(Amount) as TotalAmount&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;from Sales&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;where ProductID = 1&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;group by &lt;span style="font-weight: bold;"&gt;all &lt;/span&gt;CustomerID&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;CustomerID  TotalAmount&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;----------- ---------------------&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;1           1010.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2           NULL&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;3           7730.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;Warning: Null value is eliminated by an aggregate or other SET operation.&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;(3 row(s) affected)&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
Notice that now &lt;span style="font-style: italic;"&gt;all &lt;/span&gt;Customers are now returned, and a NULL is shown as the TotalAmount for Customer #2, who has no orders for ProductID #1 ...  Even though though the WHERE clause seems to indicate that we should &lt;span style="font-style: italic;"&gt;not&lt;/span&gt; be seeing customer #2 in the results!&lt;br /&gt;
&lt;br /&gt;
The ALL option basically says "ignore the WHERE clause when doing the GROUPING, but still apply it for any aggregate functions".   So, in this case, the WHERE clause is not considered when generating the population of CustomerID values, but it is applied when calculating the SUM.  This is very much like our first solution, where we removed the WHERE clause completely, and used a SUM(CASE...) expression to conditionally calculate the aggregate.  &lt;br /&gt;
&lt;br /&gt;
Values that are excluded from the aggregation according to the WHERE clause have NULL values returned, as you can see in the result.  A simple ISNULL() or COALESCE() will allow us to return 0 instead of NULL:&lt;br /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;select CustomerID, isnull(sum(Amount),0) as TotalAmount&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;from Sales&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;where ProductID = 1&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;group by all CustomerID&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;CustomerID  TotalAmount&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;----------- ---------------------&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;1           1010.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2           0.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;3           7730.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;Warning: Null value is eliminated by an aggregate or other SET operation.&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;(3 row(s) affected)&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
Notice that the warning about NULL being aggregated still displays, since that is the standard behavior in SQL Server when you calculate an aggregate on a NULL value.  You can turn these warnings off if you like for the during of the batch by issuing a &lt;span style="font-family: Courier New;"&gt;set ANSI_WARNINGS off &lt;/span&gt;command before your SELECT.&lt;br /&gt;
&lt;br /&gt;
GROUP BY ALL is kind of obscure and neat to know, but not really useful in most situations since there are usually easier or better ways to get this result.  For one thing, this won't work if we want all Customers to be displayed, since a customer &lt;span style="font-style: italic;"&gt;must have at least one order &lt;/span&gt;to show up in the result.  If we want to see all customers, even those that have never ordered, we would need to do a LEFT OUTER JOIN from the Customers table to our Orders aggregate SELECT:&lt;br /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;create table Customers (CustomerID int primary key)&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;insert into Customers&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;select 1 union all &lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;select 2 union all &lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;select 3 union all &lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;select 4&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;-- Notice that we have 4 customers, but our Sales data has sales for only 3.&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;select c.customerID, isnull(s.TotalAmount,0) as TotalAmount&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;from Customers c&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;left outer join &lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    (select customerID, sum(Amount) as TotalAmount&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;     from Sales&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;     where ProductID = 1&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    group by customerID) s on c.customerID = s.customerID&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    &lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;customerID  TotalAmount&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;----------- ---------------------&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;1           1010.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2           0.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;3           7730.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;4           0.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;(4 row(s) affected)&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
That is typically the standard way to return data for an entire population, regardless of existing transactions.  GROUP BY ALL gets us close, but if a new customer has never made an Order, they will never show up in the results.   Of course, depending on your needs, that may be what you want.&lt;br /&gt;
&lt;br /&gt;
Another limitation is we can not use GROUP BY ALL if we want to return a grand total for all orders, along with the total just for ProductID #1.  For example, using the SUM(CASE...) expression along with a regular SUM(), we can do this:&lt;br /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;select CustomerID, sum(case when ProductID=1 then Amount else 0 end) as Product1Amount,&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    sum(Amount) as TotalAmount&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;from Sales&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;group by CustomerID&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;CustomerID  Product1Amount        TotalAmount&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;----------- --------------------- ---------------------&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;1           1010.00               3310.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2           0.00                  5275.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;3           7730.00               10030.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;(3 row(s) affected)&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;/div&gt;
&lt;br /&gt;
That lets us calculate two different totals all in one pass through the table.  However, we cannot translate that using GROUP BY ALL, because while we will be able to return the Product1Amount, there would be no easy way to also get the TotalAmount for all products without an additional join or sub-query.&lt;br /&gt;
&lt;br /&gt;
. . .&lt;br /&gt;
&lt;br /&gt;
So, that's the story with GROUP BY ALL. It is interesting, and not widely well-known, and may even make for a good interview question if you really want to see how much SQL a candidate knows.  But for practical purposes, it is pretty rarely used and there are generally better ways to get the same results more easily or more efficiently.&lt;br /&gt;
&lt;br /&gt;
Anyone have a good situation or an example of where GROUP BY ALL really worked well for you?  Be sure to share your experiences in the comments.&lt;img src="http://weblogs.sqlteam.com/jeffs/aggbug/60588.aspx" width="1" height="1" /&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/YB_PnYFJL9f9gYCOpI1f8C55OW4/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/YB_PnYFJL9f9gYCOpI1f8C55OW4/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/YB_PnYFJL9f9gYCOpI1f8C55OW4/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/YB_PnYFJL9f9gYCOpI1f8C55OW4/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=SsSsWVagsFo:G9SYwgVWoI4:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=SsSsWVagsFo:G9SYwgVWoI4:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?i=SsSsWVagsFo:G9SYwgVWoI4:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=SsSsWVagsFo:G9SYwgVWoI4:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?i=SsSsWVagsFo:G9SYwgVWoI4:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=SsSsWVagsFo:G9SYwgVWoI4:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?i=SsSsWVagsFo:G9SYwgVWoI4:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=SsSsWVagsFo:G9SYwgVWoI4:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</description>
            <dc:creator>Jeff Smith</dc:creator>
            <guid>http://weblogs.sqlteam.com/jeffs/archive/2008/05/05/group-by-all.aspx</guid>
            <pubDate>Mon, 05 May 2008 16:25:24 GMT</pubDate>
            <wfw:comment>http://weblogs.sqlteam.com/jeffs/comments/60588.aspx</wfw:comment>
            <comments>http://weblogs.sqlteam.com/jeffs/archive/2008/05/05/group-by-all.aspx#feedback</comments>
            <slash:comments>5</slash:comments>
            <wfw:commentRss>http://weblogs.sqlteam.com/jeffs/comments/commentRss/60588.aspx</wfw:commentRss>
            <trackback:ping>http://weblogs.sqlteam.com/jeffs/services/trackbacks/60588.aspx</trackback:ping>
        </item>
        <item>
            <title>UNPIVOT: Normalizing data on the fly</title>
            <link>http://weblogs.sqlteam.com/jeffs/archive/2008/04/23/unpivot.aspx</link>
            <description>Everyone seems to want to &lt;a href="http://weblogs.sqlteam.com/jeffs/category/156.aspx"&gt;"pivot" or "cross tab"&lt;/a&gt; data, but knowing how to do the opposite is equally important.  In fact, I would argue that the skill of "unpivoting" data is more useful and more important and more relevant to a SQL programmer, since pivoting results in denormalized data, while unpivoting can transform non-normalized data into a normalized result set.  We all know that there's lots of bad databases designs out there, so this can be a handy technique to know.  &lt;br /&gt;
&lt;br /&gt;
Of course, even a well designed, fully normalized database can still benefit from "unpivoting" from time to time, so let's take a look at some common situations and some of the options we have to handle this at our disposal.  We will focus on some traditional SQL techniques to do this, and then take a close look at the &lt;span style="font-weight: bold;"&gt;UNPIVOT &lt;/span&gt;operator that was introduced with SQL Server 2005.&lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-weight: bold;"&gt;Example #1:  A Bad database design&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
Let's start with a commonly bad table design, in which someone has decided to relate a client to multiple contacts by designing their client table like this:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="text-align: left; margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;create table Clients &lt;br /&gt;
(   &lt;br /&gt;
    clientID int primary key, &lt;br /&gt;
    clientName varchar(100), &lt;br /&gt;
    contact1 int, &lt;br /&gt;
    contact2 int, &lt;br /&gt;
    contact3 int, &lt;br /&gt;
    contact4 int&lt;br /&gt;
)&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;insert into Clients&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;select 1,'ABC Corp',1,34,2,null union all&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;select 2,'DEF Foundation',6,2,8,9 union all&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;select 3,'GHI Inc.',5,9,null,null union all&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;select 4,'XYZ Industries',24,null,6,null&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;clientID    clientName           contact1    contact2    contact3    contact4&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;----------- -------------------- ----------- ----------- ----------- -----------&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;1           ABC Corp             1           34          2           NULL&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2           DEF Foundation       6           2           8           9&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;3           GHI Inc.             5           9           NULL        NULL&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;4           XYZ Industries       24          NULL        6           NULL&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;(4 row(s) affected)&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;span style="font-style: italic;"&gt;(Note: For brevity, I am not including the contact table here, nor the foreign key constraints.  Of course, with this table design, it would probably be pretty unlikely to find such constraints in the database anyway)&lt;/span&gt;&lt;br style="font-style: italic;" /&gt;
&lt;br /&gt;
With this design, it is not very easy or efficient to get a count of all contacts for each client, or to find out which contacts are related to which clients.   One thing we can do, however is to "unpivot" this table in a query that returns 1 row per ClientID/ContactID combination.  With that result set, we can easily now reference the table as if it were normalized and we can get the information we need.&lt;br /&gt;
&lt;br /&gt;
One way to do is to use UNION ALL to return each row in the clients table 4 times, and each time return a different contactID column:&lt;br /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;select clientID, contact1 as ContactID&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;from clients&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;where contact1 is not null&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;union all&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;select clientID, contact2 as ContactID&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;from clients&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;where contact2 is not null&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;union all&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;select clientID, contact3 as ContactID&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;from clients&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;where contact3 is not null&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;union all&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;select clientID, contact4 as ContactID&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;from clients&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;where contact4 is not null&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;clientID    ContactID&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;----------- -----------&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;1           1&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2           6&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;3           5&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;4           24&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;1           34&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2           2&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;3           9&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;1           2&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2           8&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;4           6&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2           9&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;(11 row(s) affected)&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;/div&gt;
Another option is to CROSS JOIN the Clients table with a table or resultset that returns 4 rows, which also effectively returns each row in the clients table 4 times.  For each of the 4 values in the table we are cross joining, we grab a different contact column:&lt;br /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;select *&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;from&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;(&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    select c.clientID,&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;       case n.n when 1 then c.contact1&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;            when 2 then c.contact2&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;            when 3 then c.contact3&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;            when 4 then c.contact4 end as ContactID&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    from&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;        clients c&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    cross join&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;        (select 1 as n union all select 2 union all select 3 union all select 4) n&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;)    &lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    x&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;where&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    x.ContactID is not null&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;clientID    ContactID&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;----------- -----------&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;1           1&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;1           34&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;1           2&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2           6&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2           2&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2           8&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2           9&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;3           5&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;3           9&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;4           24&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;4           6&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;(11 row(s) affected)&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New; font-style: italic;" /&gt;
&lt;/div&gt;
&lt;span style="font-style: italic;"&gt;(Note that you can use a permanent table of Numbers in your database instead of generating it on the fly with a UNION, as shown)&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
Finally, however, there is an even eaiser way to handle this: the &lt;span style="font-weight: bold;"&gt;UNPIVOT &lt;/span&gt;operator, new with SQL 2005.  &lt;span style="font-weight: bold;"&gt;UNPIVOT &lt;/span&gt;works very efficiently and really allows you to handle this exact situation quite easily:&lt;br /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;select clientID, Contact.ContactID&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;from clients&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;unpivot (ContactID for ContactNumber in (contact1, contact2,contact3,contact4)) as Contact&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;clientID    ContactID&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;----------- -----------&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;1           1&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;1           34&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;1           2&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2           6&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2           2&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2           8&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2           9&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;3           5&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;3           9&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;4           24&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;4           6&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;(11 row(s) affected)&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;/div&gt;
&lt;br /&gt;
Much shorter to write, and more efficient to execute as well.  &lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-weight: bold;"&gt;Taking a Closer Look at UNPIVOT&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
The UNPIVOT operator is tricky to get a feel for, however, so let's take a look at it.&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;unpivot (ContactID for ContactNumber in (contact1, contact2,contact3,contact4)) &lt;span style="font-weight: bold; background-color: rgb(255, 255, 153);"&gt;as Contact&lt;/span&gt;&lt;/span&gt;&lt;br style="background-color: rgb(255, 255, 153);" /&gt;
&lt;/div&gt;
&lt;br style="background-color: rgb(255, 255, 153);" /&gt;
&lt;span style="background-color: rgb(255, 255, 153);"&gt; &lt;/span&gt;First, the "As Contact" at the end is just labeling the entire unpivot result set with an alias, just as you must alias a derived table.  Each column returned by the pivot operator can be referenced by the alias if necessary.&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;unpivot (&lt;span style="background-color: rgb(255, 255, 153);"&gt;ContactID&lt;/span&gt; for&lt;span style="font-weight: bold; font-family: Courier New;"&gt; &lt;/span&gt;ContactNumber &lt;span style="font-weight: bold; background-color: rgb(255, 255, 153);"&gt;in (contact1, contact2,contact3,contact4)&lt;/span&gt;) as Contact&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
The "ContactID for" part says that we want to return a column called "ContactID" for each unpivoted row.  The IN() list is the columns that we are unpivoting; the values in the 4 columns listed here will be assigned to the ContactID column in the result.  So, the first time a particular row is unpivoted, the value of the 'contact1' column is assigned to ContactID, the next time it is the 'contact2' column, then 'contact3', and then finally 'contact4'.  Then, the next row is processed and it all begins again.  &lt;br /&gt;
&lt;br /&gt;
Thus, because we are unpivoting 4 values, we know that the result of the unpivot will have 4 times as many rows as the source data.&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;unpivot (ContactID &lt;span style="font-weight: bold; background-color: rgb(255, 255, 153);"&gt;for ContactNumber&lt;/span&gt; in (contact1, contact2,contact3,contact4)) as Contact&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
UNPIVOT returns an additional column as well, which contains the name of the column that was used to produce each unpivoted row.   Here, we have specified that to be called ContactNumber.  Note that we actually did not return ContactNumber in our example, be we can easily add that in so you can see how it works:&lt;br /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;select clientID,&lt;span style="font-weight: bold;"&gt; Contact.ContactNumber,&lt;/span&gt; Contact.ContactID&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;from clients&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;unpivot (ContactID for ContactNumber in (contact1, contact2,contact3,contact4)) as Contact&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;clientID    ContactNumber           ContactID&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;----------- ----------------------- -----------&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;1           contact1                1&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;1           contact2                34&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;1           contact3                2&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2           contact1                6&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2           contact2                2&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2           contact3                8&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2           contact4                9&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;3           contact1                5&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;3           contact2                9&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;4           contact1                24&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;4           contact3                6&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;(11 row(s) affected)&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
So, you can see that the code to write is very short, but a little difficult to grasp at first.  In the end, though, we are able to take a bad table design and easily "fix it", at least temporarily, so that we can query it using simple and standard SQL statements to get what we need.&lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-weight: bold;"&gt;Example #2:  Normalizing a Transaction Table&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
Here's another common example:&lt;br /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;create table Transactions&lt;br /&gt;
(&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    TranDate datetime,&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    Account varchar(10),&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    BudgetAmount money,&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    ActualAmount money,&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    ProjectionAmount money,&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    primary key (TranDate, Account)&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;)&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;go&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;insert into Transactions&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;select '2008-01-01','0001',354,65,58 union all&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;select '2008-01-02','0001',14,65,34 union all&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;select '2008-01-03','0001',0,65,622 union all&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;select '2008-01-04','0001',9,32,84&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;go&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;TranDate                Account    BudgetAmount          ActualAmount          ProjectionAmount&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;----------------------- ---------- --------------------- --------------------- ---------------------&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-01-01 00:00:00.000 0001       354.00                65.00                 58.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-01-02 00:00:00.000 0001       14.00                 65.00                 34.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-01-03 00:00:00.000 0001       0.00                  65.00                 622.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-01-04 00:00:00.000 0001       9.00                  32.00                 84.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;(4 row(s) affected)&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
Notice that we have different columns for Budget, Actual and Projection, which is not really a great database design.  Much better would be to break this data out so that we have a single 'Amount' column and a 'TransactionType' column that specifies the type of each transaction.  We can transform our Transactions tables into this format using UNION ALL:&lt;br /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;select TranDate, Account, 'BudgetAmount' as Type, BudgetAmount as Amount from transactions&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;union all&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;select TranDate, Account, 'ActualAmount' as Type, ActualAmount as Amount from transactions&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;union all&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;select TranDate, Account, 'ProjectionAmount' as Type, ProjectionAmount as Amount from transactions&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;TranDate                Account    Type             Amount&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;----------------------- ---------- ---------------- ---------------------&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-01-01 00:00:00.000 0001       BudgetAmount     354.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-01-02 00:00:00.000 0001       BudgetAmount     14.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-01-03 00:00:00.000 0001       BudgetAmount     0.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-01-04 00:00:00.000 0001       BudgetAmount     9.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-01-01 00:00:00.000 0001       ActualAmount     65.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-01-02 00:00:00.000 0001       ActualAmount     65.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-01-03 00:00:00.000 0001       ActualAmount     65.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-01-04 00:00:00.000 0001       ActualAmount     32.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-01-01 00:00:00.000 0001       ProjectionAmount 58.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-01-02 00:00:00.000 0001       ProjectionAmount 34.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-01-03 00:00:00.000 0001       ProjectionAmount 622.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-01-04 00:00:00.000 0001       ProjectionAmount 84.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;(12 row(s) affected)&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
Or, we can use the &lt;span style="font-weight: bold;"&gt;UNPIVOT &lt;/span&gt;operator to do the same much easier:&lt;br /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;select TranDate, Account, Type, Amount&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;from Transactions&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;unpivot (Amount for Type in (BudgetAmount, ActualAmount, ProjectionAmount)) as Amount&lt;br /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;/span&gt;&lt;span style="font-family: Courier New;"&gt;TranDate                Account   Type                     Amount&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;----------------------- --------- ------------------------ ---------------------&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-01-01 00:00:00.000 0001      BudgetAmount             354.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-01-01 00:00:00.000 0001      ActualAmount             65.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-01-01 00:00:00.000 0001      ProjectionAmount         58.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-01-02 00:00:00.000 0001      BudgetAmount             14.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-01-02 00:00:00.000 0001      ActualAmount             65.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-01-02 00:00:00.000 0001      ProjectionAmount         34.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-01-03 00:00:00.000 0001      BudgetAmount             0.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-01-03 00:00:00.000 0001      ActualAmount             65.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-01-03 00:00:00.000 0001      ProjectionAmount         622.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-01-04 00:00:00.000 0001      BudgetAmount             9.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-01-04 00:00:00.000 0001      ActualAmount             32.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-01-04 00:00:00.000 0001      ProjectionAmount         84.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;(12 row(s) affected)&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;span style="font-weight: bold;"&gt;Example #3: "Unsummarizing" Data&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
For those who work with accounting systems, this example may be familiar to you.   Many times, Accounting systems have "summary" tables that roll up transactional data into a structure like this:&lt;br /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;create table AccountBalances&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;(&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    CompanyID int,&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    AccountID int,&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    TransactionTypeID int,&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    Year int,&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    Period1 money,&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    Period2 money,&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    Period3 money,&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    Period4 money,&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    Period5 money,&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    Period6 money,&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    Period7 money,&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    Period8 money,&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    Period9 money,&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    Period10 money,&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    Period11 money,&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    Period12 money&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;)&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;insert into AccountBalances&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;select 1,1,1,2008,200,300,400,500,400,0,0,0,0,0,0,0 union all&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;select 1,2,1,2008,100,100,100,100,100,100,100,0,0,0,0,0 union all&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;select 1,3,1,2008,150,0,50,10,10,200,400,45,0,0,0,0&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;span style="font-style: italic;"&gt;(As before, let's not worry about those foreign key constraints)&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
These tables are often calculated when transactions are posted, or periods are closed.   Typically, many reports pull from these tables because it is much more efficient than summarizing thousands or millions of transactions, and the data is already "cross-tabbed" the way most reporting tools would like to display it.&lt;br /&gt;
&lt;br /&gt;
We can take this summarized data and "unpivot" it so we can still access the summarized data, but now it will be in a normalized structure.  All it takes is a simple &lt;span style="font-weight: bold;"&gt;UNPIVOT &lt;/span&gt;like this:&lt;br /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;select &lt;br /&gt;
       CompanyID, &lt;br /&gt;
       AccountID, &lt;br /&gt;
       TransactionTypeID, &lt;br /&gt;
       Year, &lt;br /&gt;
       substring(Period,7,2) as PeriodNo, &lt;br /&gt;
       Amount&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;from &lt;br /&gt;
       AccountBalances&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;unpivot &lt;br /&gt;
  (Amount for Period in (Period1,Period2,Period3,Period4,Period5,Period6,&lt;br /&gt;
                         Period7,Period8,Period9,Period10,Period11,Period12)&lt;br /&gt;
   ) as Amount&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;CompanyID   AccountID   TransactionTypeID Year        PeriodNo Amount&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;----------- ----------- ----------------- ----------- -------- ---------------------&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;1           1           1                 2008        1        200.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;1           1           1                 2008        2        300.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;1           1           1                 2008        3        400.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;1           1           1                 2008        4        500.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;1           1           1                 2008        5        400.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;1           1           1                 2008        6        0.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;1           1           1                 2008        7        0.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;1           1           1                 2008        8        0.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;1           1           1                 2008        9        0.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;1           1           1                 2008        10       0.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;1           1           1                 2008        11       0.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;1           1           1                 2008        12       0.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;1           2           1                 2008        1        100.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;1           2           1                 2008        2        100.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;1           2           1                 2008        3        100.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;1           2           1                 2008        4        100.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;1           2           1                 2008        5        100.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;1           2           1                 2008        6        100.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;1           2           1                 2008        7        100.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;1           2           1                 2008        8        0.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;1           2           1                 2008        9        0.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;1           2           1                 2008        10       0.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;1           2           1                 2008        11       0.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;1           2           1                 2008        12       0.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;1           3           1                 2008        1        150.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;1           3           1                 2008        2        0.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;1           3           1                 2008        3        50.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;1           3           1                 2008        4        10.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;1           3           1                 2008        5        10.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;1           3           1                 2008        6        200.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;1           3           1                 2008        7        400.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;1           3           1                 2008        8        45.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;1           3           1                 2008        9        0.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;1           3           1                 2008        10       0.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;1           3           1                 2008        11       0.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;1           3           1                 2008        12       0.00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;(36 row(s) affected)&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
We can filter so that only periods with a non-zero amount are included, and we can SELECT FROM this result set and get the exact data we need for whatever date range we want without worrying which column the actual data is in.&lt;br /&gt;
&lt;br /&gt;
This can also be done with a CROSS JOIN or a UNION ALL, but with 12 values to pivot, those options become much longer to write and &lt;span style="font-weight: bold;"&gt;UNPIVOT &lt;/span&gt;appears to be the way to go in this case.&lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-weight: bold;"&gt;Example #4: Multiple unpivots&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
Finally, let's consider a more complicated example.  Here, we have a table that stores games played between two teams, where one team is the &lt;span style="font-style: italic;"&gt;HomeTeam &lt;/span&gt;and the other is the &lt;span style="font-style: italic;"&gt;AwayTeam:&lt;/span&gt;&lt;br style="font-style: italic;" /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;create table Teams&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;(&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    TeamCode char(3) primary key not null,&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    TeamName varchar(100) not null&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;)&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;create table Games&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;(&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    GameDate datetime,&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    HomeTeam char(3) references Teams(TeamCode),&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    AwayTeam char(3) references Teams(TeamCode),&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    HomeScore int,&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    AwayScore int,&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    primary key (GameDate, HomeTeam), &lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    constraint pk2 unique (GameDate, AwayTeam),&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    check (HomeTeam &amp;lt;&amp;gt; AwayTeam)&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;)&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;insert into Teams&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;select 'BOS','Boston Red Sox' union all&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;select 'NYY','New York Yankees'&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;insert into Games&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;select '2008-04-01','BOS','NYY',3,1 union all&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;select '2008-04-02','BOS','NYY',6,4 union all&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;select '2008-04-03','BOS','NYY',2,3 union all&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;select '2008-04-08','NYY','BOS',6,0 union all&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;select '2008-04-09','NYY','BOS',2,6 union all&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;select '2008-04-10','NYY','BOS',1,10&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;GameDate                HomeTeam AwayTeam HomeScore   AwayScore&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;----------------------- -------- -------- ----------- -----------&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-01 00:00:00.000 BOS      NYY      3           1&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-02 00:00:00.000 BOS      NYY      6           4&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-03 00:00:00.000 BOS      NYY      2           3&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-08 00:00:00.000 NYY      BOS      6           0&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-09 00:00:00.000 NYY      BOS      2           6&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-10 00:00:00.000 NYY      BOS      1           10&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;(6 row(s) affected)&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
That may or may not be the best possible design for the Games table, but it is a common way to do it.  With data in that form, if we want to get the total runs scored per team across all games, or get each teams won-loss record, we need to make two passes through the table. This can be done fairly easily and efficiently with a union:&lt;br /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;select GameDate, &lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    HomeTeam as TeamCode, &lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    'Home' as HomeOrAway, &lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    HomeScore as Score, &lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    case when HomeScore &amp;gt; AwayScore then 1 else 0 end as Win,&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    case when HomeScore &amp;lt; AwayScore then 1 else 0 end as Loss,&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    case when HomeScore = AwayScore then 1 else 0 end as Tie&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;from&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    Games&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;union all&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;select GameDate, &lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    AwayTeam as TeamCode, &lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    'Away' as HomeOrAway, &lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    AwayScore as Score, &lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    case when HomeScore &amp;lt; AwayScore then 1 else 0 end as Win,&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    case when HomeScore &amp;gt; AwayScore then 1 else 0 end as Loss,&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    case when HomeScore = AwayScore then 1 else 0 end as Tie&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;from&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    Games&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    &lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;GameDate                TeamCode HomeOrAway Score       Win         Loss        Tie&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;----------------------- -------- ---------- ----------- ----------- ----------- -----&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-01 00:00:00.000 BOS      Home       3           1           0           0&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-02 00:00:00.000 BOS      Home       6           1           0           0&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-03 00:00:00.000 BOS      Home       2           0           1           0&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-08 00:00:00.000 NYY      Home       6           1           0           0&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-09 00:00:00.000 NYY      Home       2           0           1           0&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-10 00:00:00.000 NYY      Home       1           0           1           0&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-01 00:00:00.000 NYY      Away       1           0           1           0&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-02 00:00:00.000 NYY      Away       4           0           1           0&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-03 00:00:00.000 NYY      Away       3           1           0           0&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-08 00:00:00.000 BOS      Away       0           0           1           0&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-09 00:00:00.000 BOS      Away       6           1           0           0&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-10 00:00:00.000 BOS      Away       10          1           0           0&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;(12 row(s) affected)&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
Now, how can we do this with &lt;span style="font-weight: bold;"&gt;UNPIVOT&lt;/span&gt;?  This example is a bit more complicated, because we are "unpivoting" not only the score, but also the TeamCode, and we are also calculating a few extra columns (Win, Loss and Tie).&lt;br /&gt;
&lt;br /&gt;
So, can we &lt;span style="font-weight: bold;"&gt;UNPIVOT &lt;/span&gt;more than 1 column?  Let's start simply and do things one at a time.  First, let's &lt;span style="font-weight: bold;"&gt;UNPIVOT &lt;/span&gt;just the TeamCode:&lt;br /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;select GameDate, HomeOrAway, Team&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;from Games&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;unpivot (Team for HomeOrAway in (HomeTeam, AwayTeam)) as Team&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;GameDate                HomeOrAway     Team&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;----------------------- -------------- ----&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-01 00:00:00.000 HomeTeam       BOS&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-01 00:00:00.000 AwayTeam       NYY&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-02 00:00:00.000 HomeTeam       BOS&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-02 00:00:00.000 AwayTeam       NYY&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-03 00:00:00.000 HomeTeam       BOS&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-03 00:00:00.000 AwayTeam       NYY&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-08 00:00:00.000 HomeTeam       NYY&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-08 00:00:00.000 AwayTeam       BOS&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-09 00:00:00.000 HomeTeam       NYY&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-09 00:00:00.000 AwayTeam       BOS&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-10 00:00:00.000 HomeTeam       NYY&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-10 00:00:00.000 AwayTeam       BOS&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;(12 row(s) affected)&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
OK, so far so good.  Now, how do we get the score for the team as well?  Let's add another &lt;span style="font-weight: bold;"&gt;UNPIVOT &lt;/span&gt;clause to the SELECT, this time for Score:&lt;br /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;select GameDate, HomeOrAway, Team, Score&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;from Games&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;unpivot (Team for HomeOrAway in (HomeTeam, AwayTeam)) as Team&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New; font-weight: bold;"&gt;unpivot (Score for HomeOrAway in (HomeScore, AwayScore)) as Score&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New; color: rgb(128, 0, 0);"&gt;Msg 265, Level 16, State 1, Line 1&lt;/span&gt;&lt;br style="font-family: Courier New; color: rgb(128, 0, 0);" /&gt;
&lt;span style="font-family: Courier New; color: rgb(128, 0, 0);"&gt;The column name "HomeOrAway" specified in the UNPIVOT operator conflicts with the existing column name in the UNPIVOT argument.&lt;/span&gt;&lt;br style="font-family: Courier New; color: rgb(128, 0, 0);" /&gt;
&lt;span style="font-family: Courier New; color: rgb(128, 0, 0);"&gt;Msg 8156, Level 16, State 1, Line 1&lt;/span&gt;&lt;br style="font-family: Courier New; color: rgb(128, 0, 0);" /&gt;
&lt;span style="font-family: Courier New; color: rgb(128, 0, 0);"&gt;The column 'HomeOrAway' was specified multiple times for 'Score'.&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
Hmmm. OK, we cannot specify the same column for both pivots.  That is just an alias specification, no big deal, so let's just alias it as "HomeOrAway2" :&lt;br /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;select GameDate, HomeOrAway, Team, Score&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;from Games&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;unpivot (Team for HomeOrAway in (HomeTeam, AwayTeam)) as Team&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;unpivot (Score for &lt;span style="font-weight: bold;"&gt;HomeOrAway2 &lt;/span&gt;in (HomeScore, AwayScore)) as Score&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;GameDate                HomeOrAway Team Score&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;----------------------- ---------- ---- -----------&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-01 00:00:00.000 HomeTeam   BOS  3&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-01 00:00:00.000 HomeTeam   BOS  1&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-01 00:00:00.000 AwayTeam   NYY  3&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-01 00:00:00.000 AwayTeam   NYY  1&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-02 00:00:00.000 HomeTeam   BOS  6&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-02 00:00:00.000 HomeTeam   BOS  4&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-02 00:00:00.000 AwayTeam   NYY  6&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-02 00:00:00.000 AwayTeam   NYY  4&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-03 00:00:00.000 HomeTeam   BOS  2&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-03 00:00:00.000 HomeTeam   BOS  3&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-03 00:00:00.000 AwayTeam   NYY  2&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-03 00:00:00.000 AwayTeam   NYY  3&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-08 00:00:00.000 HomeTeam   NYY  6&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-08 00:00:00.000 HomeTeam   NYY  0&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-08 00:00:00.000 AwayTeam   BOS  6&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-08 00:00:00.000 AwayTeam   BOS  0&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-09 00:00:00.000 HomeTeam   NYY  2&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-09 00:00:00.000 HomeTeam   NYY  6&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-09 00:00:00.000 AwayTeam   BOS  2&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-09 00:00:00.000 AwayTeam   BOS  6&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-10 00:00:00.000 HomeTeam   NYY  1&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-10 00:00:00.000 HomeTeam   NYY  10&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-10 00:00:00.000 AwayTeam   BOS  1&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-10 00:00:00.000 AwayTeam   BOS  10&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;(24 row(s) affected)&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
Holy schnikies, it works!  We can specify more than one &lt;span style="font-weight: bold;"&gt;UNPIVOT &lt;/span&gt;clause for the same SQL statement!  Who would have thunk it?&lt;br /&gt;
&lt;br /&gt;
Uh oh -- wait a second.  We have 24 results returned.  We should have only 12.  Something is not right here.  Remember when we aliased our second unpivot column as "HomeOrAway2"?  We did not return that anywhere in our results.  Let's add that in and take a look:&lt;br /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;select GameDate, HomeOrAway, HomeOrAway2, Team, Score&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;from Games&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;unpivot (Team for HomeOrAway in (HomeTeam, AwayTeam)) as Team&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;unpivot (Score for HomeOrAway2 in (HomeScore, AwayScore)) as Score&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;GameDate                HomeOrAway HomeOrAway2 Team Score&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;----------------------- ---------- ----------- ---- -----------&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-01 00:00:00.000 HomeTeam   HomeScore   BOS  3&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-01 00:00:00.000 HomeTeam   AwayScore   BOS  1&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-01 00:00:00.000 AwayTeam   HomeScore   NYY  3&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-01 00:00:00.000 AwayTeam   AwayScore   NYY  1&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-02 00:00:00.000 HomeTeam   HomeScore   BOS  6&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-02 00:00:00.000 HomeTeam   AwayScore   BOS  4&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-02 00:00:00.000 AwayTeam   HomeScore   NYY  6&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-02 00:00:00.000 AwayTeam   AwayScore   NYY  4&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-03 00:00:00.000 HomeTeam   HomeScore   BOS  2&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-03 00:00:00.000 HomeTeam   AwayScore   BOS  3&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-03 00:00:00.000 AwayTeam   HomeScore   NYY  2&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-03 00:00:00.000 AwayTeam   AwayScore   NYY  3&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-08 00:00:00.000 HomeTeam   HomeScore   NYY  6&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-08 00:00:00.000 HomeTeam   AwayScore   NYY  0&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-08 00:00:00.000 AwayTeam   HomeScore   BOS  6&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-08 00:00:00.000 AwayTeam   AwayScore   BOS  0&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-09 00:00:00.000 HomeTeam   HomeScore   NYY  2&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-09 00:00:00.000 HomeTeam   AwayScore   NYY  6&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-09 00:00:00.000 AwayTeam   HomeScore   BOS  2&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-09 00:00:00.000 AwayTeam   AwayScore   BOS  6&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-10 00:00:00.000 HomeTeam   HomeScore   NYY  1&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-10 00:00:00.000 HomeTeam   AwayScore   NYY  10&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-10 00:00:00.000 AwayTeam   HomeScore   BOS  1&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-10 00:00:00.000 AwayTeam   AwayScore   BOS  10&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;(24 row(s) affected)&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
Notice what is happening here. Without an UNPIVOT, we had 6 rows.  We added one UNPIVOT to pivot two values, and got 12 rows.  Then, we added another pivot, again with two values, and got back 24.  Looking at the results, we can see that the single game at '2008-04-01' was unpivoted into 4 rows.  So, while we can unpivot multiple times, &lt;span style="font-style: italic;"&gt;there is no relation between each UNPIVOT.  &lt;/span&gt;That is, notice there is no correlation between HomeOrAway and HomeOrAway2.  Let's look at just that first game:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;GameDate                HomeOrAway HomeOrAway2 Team Score&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;----------------------- ---------- ----------- ---- -----------&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-01 00:00:00.000 HomeTeam   HomeScore   BOS  3&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-01 00:00:00.000 HomeTeam   AwayScore   BOS  1&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-01 00:00:00.000 AwayTeam   HomeScore   NYY  3&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-01 00:00:00.000 AwayTeam   AwayScore   NYY  1&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;/div&gt;
&lt;br /&gt;
Both scores, home and away, are specified for each team -- even if it is not their matching score.  What we really want is only these two lines:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;GameDate                HomeOrAway HomeOrAway2 Team Score&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;----------------------- ---------- ----------- ---- -----------&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-01 00:00:00.000 HomeTeam   HomeScore   BOS  3&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New; text-decoration: line-through;"&gt;2008-04-01 00:00:00.000 HomeTeam   AwayScore   BOS  1&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New; text-decoration: line-through;"&gt;2008-04-01 00:00:00.000 AwayTeam   HomeScore   NYY  3&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2008-04-01 00:00:00.000 AwayTeam   AwayScore   NYY  1&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;/div&gt;
How can we get there?  Well, there's always a WHERE clause!  We can simply filter our result so that the Left 4 characters of HomeOrAway matches the left 4 characters of HomeOrAway2.  After all, there is nothing magical about these unpivoted columns, they are data just like anything else.&lt;br /&gt;
&lt;br /&gt;
So, we can now write:&lt;br /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;select GameDate, left(HomeOrAway,4) as HomeOrAway, Team, Score&lt;br /&gt;
from Games&lt;br /&gt;
unpivot (Team for HomeOrAway in (HomeTeam, AwayTeam)) as Team&lt;br /&gt;
unpivot (Score for HomeOrAway2 in (HomeScore, AwayScore)) as Score&lt;br /&gt;
where left(HomeOrAway,4) = left(HomeOrAway2,4)&lt;/span&gt;&lt;span style="font-family: Courier New;"&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;GameDate                HomeOrAway Team Score&lt;br /&gt;
----------------------- ---------- ---- -----------&lt;br /&gt;
2008-04-01 00:00:00.000 Home       BOS  3&lt;br /&gt;
2008-04-01 00:00:00.000 Away       NYY  1&lt;br /&gt;
2008-04-02 00:00:00.000 Home       BOS  6&lt;br /&gt;
2008-04-02 00:00:00.000 Away       NYY  4&lt;br /&gt;
2008-04-03 00:00:00.000 Home       BOS  2&lt;br /&gt;
2008-04-03 00:00:00.000 Away       NYY  3&lt;br /&gt;
2008-04-08 00:00:00.000 Home       NYY  6&lt;br /&gt;
2008-04-08 00:00:00.000 Away       BOS  0&lt;br /&gt;
2008-04-09 00:00:00.000 Home       NYY  2&lt;br /&gt;
2008-04-09 00:00:00.000 Away       BOS  6&lt;br /&gt;
2008-04-10 00:00:00.000 Home       NYY  1&lt;br /&gt;
2008-04-10 00:00:00.000 Away       BOS  10&lt;br /&gt;
&lt;br /&gt;
(12 row(s) affected)&lt;br /&gt;
&lt;/span&gt;&lt;span style="font-family: Courier New;"&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
Now we are back to 12 results returned, the Homes and Aways match up, and things look good!  Hey, we just pulled off 2 unpivots at once, not bad!  From here, we can add our Win, Loss and Tie calculations as well.  Note that we also have removed "HomeOrAway2" from the SELECT, since we don't need to see that column in our result.  Finally, we are returning only the left 4 characters of HomeTeam or AwayTeam, so that the word 'Home' or 'Away' is returned in the HomeOrAway column.&lt;br /&gt;
&lt;br /&gt;
This seems like quite a bit of work.  Is &lt;span style="font-weight: bold;"&gt;UNPIVOT &lt;/span&gt;really the way to go in this scenario?  Maybe not.  I have not done full performance testing of &lt;span style="font-weight: bold;"&gt;UNPIVOT &lt;/span&gt;versus the other options shown (this article is way too long as it is!) but as always you should fully test your options and decide which offers the best performance and code simplicity.&lt;br /&gt;
&lt;br /&gt;
Another option if you want to unpivot more than 1 column is to just use one &lt;span style="font-weight: bold;"&gt;UNPIVOT &lt;/span&gt;cause, but then use CASE expressions to return the values you need.  For example:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;select GameDate, left(HomeOrAway,4) as HomeOrAway, &lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    Team, &lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New; font-weight: bold;"&gt;    case when HomeOrAway='HomeTeam' then HomeScore else AwayScore end as Score&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;from Games&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;unpivot (Team for HomeOrAway in (HomeTeam, AwayTeam)) as Team&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;span style="font-weight: bold;"&gt;&lt;/span&gt;&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;GameDate                HomeOrAway Team Score&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt; ----------------------- ---------- ---- -----------&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt; 2008-04-01 00:00:00.000 Home       BOS  3&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt; 2008-04-01 00:00:00.000 Away       NYY  1&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt; 2008-04-02 00:00:00.000 Home       BOS  6&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt; 2008-04-02 00:00:00.000 Away       NYY  4&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt; 2008-04-03 00:00:00.000 Home       BOS  2&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt; 2008-04-03 00:00:00.000 Away       NYY  3&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt; 2008-04-08 00:00:00.000 Home       NYY  6&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt; 2008-04-08 00:00:00.000 Away       BOS  0&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt; 2008-04-09 00:00:00.000 Home       NYY  2&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt; 2008-04-09 00:00:00.000 Away       BOS  6&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt; 2008-04-10 00:00:00.000 Home       NYY  1&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt; 2008-04-10 00:00:00.000 Away       BOS  10&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt; (12 row(s) affected)&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;/div&gt;
&lt;br /&gt;
This is kind of a combination of using &lt;span style="font-weight: bold;"&gt;UNPIVOT &lt;/span&gt;and a CROSS JOIN; the &lt;span style="font-weight: bold;"&gt;UNPIVOT &lt;/span&gt;doubles-up the results returned just as a CROSS JOIN would, and we just use CASE to return the column that we need depending if the current row is a "HomeTeam" or an "AwayTeam" row.    This is a fairly good solution and is probably better than using multiple &lt;span style="font-weight: bold;"&gt;UNPIVOT &lt;/span&gt;clauses.  &lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-weight: bold;"&gt;Summary&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-weight: bold;"&gt;UNPIVOT &lt;/span&gt;is a really handy operator, but it can be tricky to use.  You can &lt;span style="font-weight: bold;"&gt;UNPIVOT &lt;/span&gt;more than 1 column at a time, but beware of how it works -- each &lt;span style="font-weight: bold;"&gt;UNPIVOT &lt;/span&gt;uses it's own "pivot values" and you may get many more results than you intend. Thus, you may need to add criteria to your SELECT to ensure that only the results you are looking for are returned.  Alternatively, you can use one &lt;span style="font-weight: bold;"&gt;UNPIVOT &lt;/span&gt;along with CASE expressions to achieve the same results.&lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-weight: bold;"&gt;UNPIVOT &lt;/span&gt;is great, but the main thing is that you are at the mercy of the column names; the naming conventions of the columns used becomes data, and that can really complicate things.   In general, we want to keep our object names and our data seperate, so keep that limitation in mind if you wish to employ &lt;span style="font-weight: bold;"&gt;UNPIVOT &lt;/span&gt;in your code.   In some cases, an old-school UNION ALL or CROSS JOIN may be a preferrable solution.&lt;img src="http://weblogs.sqlteam.com/jeffs/aggbug/60573.aspx" width="1" height="1" /&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/C4u5WNBCV2_9emebSsG74X0KqQ4/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/C4u5WNBCV2_9emebSsG74X0KqQ4/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/C4u5WNBCV2_9emebSsG74X0KqQ4/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/C4u5WNBCV2_9emebSsG74X0KqQ4/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=r53EG_I68uk:pQuu-Yc_VII:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=r53EG_I68uk:pQuu-Yc_VII:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?i=r53EG_I68uk:pQuu-Yc_VII:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=r53EG_I68uk:pQuu-Yc_VII:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?i=r53EG_I68uk:pQuu-Yc_VII:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=r53EG_I68uk:pQuu-Yc_VII:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?i=r53EG_I68uk:pQuu-Yc_VII:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?a=r53EG_I68uk:pQuu-Yc_VII:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JeffsSqlServerWeblog?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</description>
            <dc:creator>Jeff Smith</dc:creator>
            <guid>http://weblogs.sqlteam.com/jeffs/archive/2008/04/23/unpivot.aspx</guid>
            <pubDate>Wed, 23 Apr 2008 14:33:15 GMT</pubDate>
            <wfw:comment>http://weblogs.sqlteam.com/jeffs/comments/60573.aspx</wfw:comment>
            <comments>http://weblogs.sqlteam.com/jeffs/archive/2008/04/23/unpivot.aspx#feedback</comments>
            <slash:comments>14</slash:comments>
            <wfw:commentRss>http://weblogs.sqlteam.com/jeffs/comments/commentRss/60573.aspx</wfw:commentRss>
            <trackback:ping>http://weblogs.sqlteam.com/jeffs/services/trackbacks/60573.aspx</trackback:ping>
        </item>
    </channel>
</rss>
