<rss version="2.0" 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/">
    <channel>
        <title>SQL Server 2005</title>
        <link>http://weblogs.sqlteam.com/jeffs/category/213.aspx</link>
        <description>Making use of the great new features in SQL 2005.  </description>
        <language>en-US</language>
        <copyright>Jeff Smith</copyright>
        <managingEditor>smith_jeffreyt@yahoo.com</managingEditor>
        <generator>Subtext Version 1.9.4.0</generator>
        <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 CompanyID, AccountID, TransactionTypeID, Year, substring(Period,7,2) as PeriodNo, Amount&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;from AccountBalances&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;unpivot (Amount for Period in (Period1,Period2,Period3,Period4,Period5,Period6,Period7,Period8,Period9,Period10,Period11,Period12)) 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;</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>11</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>
        <item>
            <title>Top N Percent per Group</title>
            <link>http://weblogs.sqlteam.com/jeffs/archive/2008/02/21/Top-N-Percent-per-Group.aspx</link>
            <description>&lt;p&gt;Here's a good question in the feedback from my post about &lt;a href="http://weblogs.sqlteam.com/jeffs/archive/2007/03/30/More-SQL-Server-2005-Solutions.aspx#38351"&gt;using the T-SQL 2005 features to return the Top N per Group&lt;/a&gt; of a result set:&lt;br /&gt;
&lt;/p&gt;
&lt;p style="margin-left: 40px;"&gt;Sani writes:&lt;/p&gt;
&lt;p style="font-style: italic; margin-left: 40px;"&gt;What about Top n Percent per Group??? I would greatly appreciate an input on that as well.&lt;/p&gt;
That's a good question, and also easily solvable.  One way to do this that I thought of was by using a combination of rank() and a count(*) &lt;a target="_blank" href="http://www.sqlteam.com/article/sql-sever-2005-using-over-with-aggregate-functions"&gt;partitioned aggregate function&lt;/a&gt;, which is also a new SQL Server 2005 feature.&lt;br /&gt;
&lt;br /&gt;
Simply calculate the rank() of each row in the group, and also the count(*) of all rows in the group. Multiply the count(*) by the percentage of rows you want returned and filter so that your rank() per group is less than that.&lt;br /&gt;
&lt;br /&gt;
Here's an example.  Suppose we want to return the newest 10% of all products per region, where the newest product has the latest "AddedDate" column.  We can write it 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;with ProductsByRegion as&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 Region, Product, AddedDate,&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;       rank() over (partition by Region order by AddedDate desc) as AddedRank,&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;       count(*) over (partition by Region) as RegionProductsCount&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;br style="font-family: Courier New;" /&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;       Region, Product, AddedDate&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;      ProductsByRegion &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;     AddedRank &amp;lt;= (RegionProductsCount * .10)&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
That's really all there is to it.   &lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-weight: bold;"&gt;Update: &lt;/span&gt;Some great feedback from &lt;span style="font-weight: bold;"&gt;Geoff N. Hiten&lt;/span&gt; in the first comment to this post shows an even easier solution:  just use the NTILE() aggregate function.  See his comment for an example.   I definitely recommend that approach, it is much simpler and certainly more straightforward since that's pretty much what NTILE is designed to do. Thanks Geoff!   (So much for my theory that Jeff's who spell their name with a "J" are always smarter than those who spell it with a "G" !)&lt;br /&gt;
&lt;br /&gt;
These partitioned functions are really amazing and so useful, they are hard to live without once you get the hang of them.&lt;img src="http://weblogs.sqlteam.com/jeffs/aggbug/60524.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Jeff Smith</dc:creator>
            <guid>http://weblogs.sqlteam.com/jeffs/archive/2008/02/21/Top-N-Percent-per-Group.aspx</guid>
            <pubDate>Thu, 21 Feb 2008 18:05:49 GMT</pubDate>
            <wfw:comment>http://weblogs.sqlteam.com/jeffs/comments/60524.aspx</wfw:comment>
            <comments>http://weblogs.sqlteam.com/jeffs/archive/2008/02/21/Top-N-Percent-per-Group.aspx#feedback</comments>
            <slash:comments>3</slash:comments>
            <wfw:commentRss>http://weblogs.sqlteam.com/jeffs/comments/commentRss/60524.aspx</wfw:commentRss>
            <trackback:ping>http://weblogs.sqlteam.com/jeffs/services/trackbacks/60524.aspx</trackback:ping>
        </item>
        <item>
            <title>Implementing Table Inheritance with SQL Server</title>
            <link>http://weblogs.sqlteam.com/jeffs/archive/2008/02/21/60522.aspx</link>
            <description>I have a new article up at SQLTeam:&lt;br /&gt;
&lt;br /&gt;
&lt;a href="http://www.sqlteam.com/article/implementing-table-inheritance-in-sql-server" target="_blank"&gt;Implementing Table Inheritance with Sql Server&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
It discusses the situation where you have multiple entities that are distinct, yet they have many attributes or relations in common.   There is an easy way to simplify your database design and your code if you use the concept of a "base table" for that common data, which is very similar to the concept of &lt;span style="font-style: italic;"&gt;Inheritance&lt;/span&gt; in Object-Oriented programming.&lt;br /&gt;
&lt;br /&gt;
I recently had to do this for a client that tracks Contributions to their foundation; there is a base set of data that all Contributions have, such as who donated, how they paid, the date a tax letter was sent, etc. But for certain contribution types we need additional information, such as if they bought an Auction Item, sponsored an Event, and so on. Thus, we set up a "base table" of Contributions and sub-tables for the different types, and set up a constraint to ensure that the right subtable is used depending on the type.  &lt;br /&gt;
&lt;br /&gt;
It sounds complicated and like it may require fancy triggers or at the very least CHECK constraints, but it is very easy to model in T-SQL -- especially in SQL Server 2005+.  The end result is that you have a less redundant database design and much less code to write to maintain that data.  &lt;br /&gt;
&lt;br /&gt;
Check it out if it sounds interesting, or helpful, or if none of this makes any sense and you're wondering what the heck I'm talking about.&lt;br /&gt;
&lt;br /&gt;&lt;img src="http://weblogs.sqlteam.com/jeffs/aggbug/60522.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Jeff Smith</dc:creator>
            <guid>http://weblogs.sqlteam.com/jeffs/archive/2008/02/21/60522.aspx</guid>
            <pubDate>Thu, 21 Feb 2008 16:05:35 GMT</pubDate>
            <wfw:comment>http://weblogs.sqlteam.com/jeffs/comments/60522.aspx</wfw:comment>
            <comments>http://weblogs.sqlteam.com/jeffs/archive/2008/02/21/60522.aspx#feedback</comments>
            <slash:comments>3</slash:comments>
            <wfw:commentRss>http://weblogs.sqlteam.com/jeffs/comments/commentRss/60522.aspx</wfw:commentRss>
            <trackback:ping>http://weblogs.sqlteam.com/jeffs/services/trackbacks/60522.aspx</trackback:ping>
        </item>
        <item>
            <title>Date Only and Time Only data types in SQL Server 2005 (without the CLR)</title>
            <link>http://weblogs.sqlteam.com/jeffs/archive/2007/10/31/sql-server-2005-date-time-only-data-types.aspx</link>
            <description>In &lt;a href="http://weblogs.sqlteam.com/jeffs/archive/2004/12/02/2959.aspx"&gt;this post&lt;/a&gt;, I showed a simple way to create simple but useful Date and Time user-defined data types in SQL Server 2000.  Here's how to do it in SQL Server 2005, without the need for CLR types or anything fancy.&lt;br /&gt;
&lt;br /&gt;
First, we must create two user defined data types:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;create type Date from dateTime&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;create type Time from dateTime&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
So, internally (and externally to our clients), these types are really just DateTime.  But, we will apply some rules to these types so that the Date data type will always be constrained to a time exactly at midnight, and the the Time data type will always be at the "base date", or 1900-01-01:&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 rule DateOnlyRule as&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;  dateAdd(dd,datediff(dd,0,@DateTime),0) = @DateTime&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;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;create rule TimeOnlyRule as&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;  datediff(dd,0,@DateTime) = 0&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 /&gt;
&lt;/div&gt;
&lt;br /&gt;
Finally, we call sp_bindrule to bind the rules to the data types:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;EXEC sp_bindrule 'DateOnlyRule', 'Date'&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;EXEC sp_bindrule 'TimeOnlyRule', 'Time'&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;/div&gt;
&lt;br /&gt;
That's it!  Now, we can create a table that uses our brand-new data types:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;create table Trans&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;(&lt;br /&gt;
 TranID int identity primary key,&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt; TranAmount money not null,&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt; TranDate Date not null,&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt; TranTime Time not null&lt;br /&gt;
)&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt; &lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
Notice that TranDate and TranTime are two separate columns here.   If we try to insert data, our rules will ensure that our TranDate and TranTime columns only contain the appropriate data, and by doing so we can simply calculate TranDate + TranTime to get the transactions DateTime.&lt;br /&gt;
&lt;br /&gt;
The following inserts will succeed:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;insert into Trans (TranAmount, TranDate, TranTime)&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;values (200, '2005-01-01', '09:00:00 AM')&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 Trans (TranAmount, TranDate, TranTime)&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;values (400, '2005-01-03', '7:50:30 PM')&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
But this will fail, since our rules are violated (getdate() returns the current date and the time):&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;insert into Trans (TranAmount, TranDate, TranTime)&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;values (200, getdate(), getdate())&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;Msg 513, Level 16, State 0, Line 1&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;A column insert or update conflicts with a rule imposed by a previous CREATE RULE statement. The statement was terminated. The conflict occurred in database 'PlayGround', table 'dbo.Trans', column 'TranDate'.&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;The statement has been terminated.&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;/div&gt;
&lt;br /&gt;
Finally, we can select from our table and simply add the two columns together to get each transaction's DateTime:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;select *, TranDate + TranTime as TranDateTime&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;from Trans&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;TranID  TranAmount   TranDate            TranTime            TranDateTime&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       200.00       2005-01-01 00:00:00 1900-01-01 09:00:00 2005-01-01 09:00:00&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2       400.00       2005-01-03 00:00:00 1900-01-01 19:50:30 2005-01-03 19:50:30&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;
The nice advantage of doing this is that you can quickly and efficiently select all transactions for a single day like this:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;select * from Trans&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;where TranDate = '2005-01-01'&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
If we stored the Date and Time in the same column, we'd need to use a less efficient range:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;select * from Trans&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;where TranDate &amp;gt;= '2005-01-01' and TranDate &amp;lt; '2005-01-02'&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
SQL 2008 does finally implement true Date and Time data types, but until then, I hope this gives you some ideas to play with. &lt;br /&gt;
&lt;br /&gt;
Notes:&lt;br /&gt;
&lt;ul&gt;
    &lt;li&gt;If you do wish to break apart DateTime data into two columns, another option is to simply use a single, standard DateTime column but then define two extra computed columns in your table, one that returns just the Date, another that returns just the Time.  Then, you can index those computed columns and reference them easily and efficiently.  If you truly wish to store just a date or just a time, however, these user defined data types are a great way to do it.  At the end of &lt;a target="_blank" href="http://www.sqlteam.com/article/using-indexed-computed-columns-to-improve-performance"&gt;this SQLTeam article&lt;/a&gt; there's a good example demonstrating this.&lt;br /&gt;
    &lt;br /&gt;
    &lt;/li&gt;
    &lt;li&gt;You could also just use the expressions for the rules in CHECK constrains on individual table columns, instead of creating the user defined data types and rules.  As &lt;a href="http://msdn2.microsoft.com/en-us/library/ms188064.aspx" target="_blank"&gt;Books On Line states&lt;/a&gt;, CREATE RULE will at some point be removed from future versions of SQL Server, so you may wish to avoid using it.  &lt;br /&gt;
    &lt;/li&gt;
&lt;/ul&gt;
&lt;span style="font-style: italic;"&gt;see also:&lt;br /&gt;
&lt;/span&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;a href="../../../../jeffs/archive/2007/10/15/time-spans-durations-sql-server.aspx" title="View Entry" id="ctl00_pageContent_Editor_Results_rprSelectionList_ctl08_HyperLink1"&gt; Working with Time Spans and Durations in SQL Server&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="../../../../jeffs/archive/2007/09/10/group-by-month-sql.aspx" title="View Entry"&gt;Group by Month (and other time periods)&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="../../../../jeffs/archive/2007/08/29/SQL-Dates-and-Times.aspx" title="View Entry" id="ctl00_pageContent_Editor_Results_rprSelectionList_ctl08_HyperLink1"&gt;Working with Date and/or Time values in SQL Server: Don't Format, Don't Convert -- just use DATETIME&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="../../../../jeffs/archive/2007/07/03/60248.aspx" title="View Entry" id="ctl00_pageContent_Editor_Results_rprSelectionList_ctl10_HyperLink1"&gt;Data Types -- The Easiest Part of Database Design&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="../../../../jeffs/archive/2007/04/13/format-date-sql-server.aspx" title="View Entry" id="ctl00_pageContent_Editor_Results_rprSelectionList_ctl06_HyperLink1"&gt;How to format a Date or DateTime in SQL Server&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="../../../../jeffs/archive/2004/12/02/2954.aspx" title="View Entry" id="ctl00_pageContent_Editor_Results_rprSelectionList_ctl06_HyperLink1"&gt;Breaking apart the DateTime datatype -- Separating Dates from Times in your Tables&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="../../../../jeffs/archive/2007/10/31/sql-server-2005-date-time-only-data-types.aspx" title="View Entry" id="ctl00_pageContent_Editor_Results_rprSelectionList_ctl04_HyperLink1"&gt;Date Only and Time Only data types in SQL Server 2005 (without the CLR)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;img src="http://weblogs.sqlteam.com/jeffs/aggbug/60387.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Jeff Smith</dc:creator>
            <guid>http://weblogs.sqlteam.com/jeffs/archive/2007/10/31/sql-server-2005-date-time-only-data-types.aspx</guid>
            <pubDate>Wed, 31 Oct 2007 13:13:16 GMT</pubDate>
            <wfw:comment>http://weblogs.sqlteam.com/jeffs/comments/60387.aspx</wfw:comment>
            <comments>http://weblogs.sqlteam.com/jeffs/archive/2007/10/31/sql-server-2005-date-time-only-data-types.aspx#feedback</comments>
            <slash:comments>8</slash:comments>
            <wfw:commentRss>http://weblogs.sqlteam.com/jeffs/comments/commentRss/60387.aspx</wfw:commentRss>
            <trackback:ping>http://weblogs.sqlteam.com/jeffs/services/trackbacks/60387.aspx</trackback:ping>
        </item>
        <item>
            <title>Taking a look at CROSS APPLY</title>
            <link>http://weblogs.sqlteam.com/jeffs/archive/2007/10/18/sql-server-cross-apply.aspx</link>
            <description>&lt;span style="font-weight: bold;"&gt;Applying a Sub-Query, Joining a Derived Table ...&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
I think the easiest way to think of CROSS APPLY is that it is like doing a CROSS JOIN with a &lt;a target="_blank" href="http://msdn2.microsoft.com/en-us/library/ms187638.aspx"&gt;correlated sub-query&lt;/a&gt; instead of a derived table.  Let's see if I can explain that .... A derived table is "self-contained", in that all tables and columns in the parent SELECT are not accessible (though variables and parameters can be referenced).  For example, consider:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;select A.*, b.X&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;from A&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;cross join (select B.X from B where &lt;span style="font-weight: bold;"&gt;B.Val=A.Val&lt;/span&gt;) b&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
That is not legal because &lt;span style="font-style: italic;"&gt;A.Val&lt;/span&gt; is out of scope within the derived table; this is because the derived table is evaluated &lt;span style="font-style: italic;"&gt;independently&lt;/span&gt; of the other tables in the SELECT.  To limit the rows in table B so that B.Val = A.Val, we must do that outside of the derived table via a join or in the criteria:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;select A.*, b.X&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;from A&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;cross join (select * from B) b&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New; font-weight: bold;"&gt;where A.Val = b.Val&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;/div&gt;
&lt;br /&gt;
(Of course, the above is equivalent to doing an INNER JOIN to the derived table, or just joining to the table B.)&lt;br /&gt;
&lt;br /&gt;
Also, keep in mind that the scope-of-derived-tables rule isn't just for CROSS JOINS, it's for all JOINS -- CROSS, INNER, OUTER and even UNION; they all use "self-contained" derived tables.&lt;br /&gt;
&lt;br /&gt;
This is in contrast to a correlated sub-query, where the parent SELECT &lt;span style="font-style: italic;"&gt;is in scope &lt;/span&gt;for the sub-query; the sub-query is evaluated for each row in the query, so the other tables and columns in the SELECT are all available:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;select A.*, (select B.X from B where &lt;span style="font-weight: bold;"&gt;B.Val=A.Val&lt;/span&gt;) as X&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;from A&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
(Note: I am ignoring for now the fact that returning multiple rows in a sub-query will return an error.)&lt;br /&gt;
&lt;br /&gt;
This is an easy way to think of the difference between CROSS JOIN and CROSS APPLY.  CROSS JOIN, as we saw, joins to a derived table; however, CROSS APPLY, despite looking like a JOIN, actually is &lt;span style="font-style: italic;"&gt;applying&lt;/span&gt; a correlated sub-query. This imposes both the advantages of a correlated sub-query but also the performance implications.&lt;br /&gt;
&lt;br /&gt;
So, we can simply rewrite our first example using CROSS APPLY 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 A.*, b.X&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;from A&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;&lt;span style="font-weight: bold;"&gt;cross apply&lt;/span&gt; (select B.X from B where &lt;span style="font-weight: bold;"&gt;B.Val=A.Val&lt;/span&gt;) b&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
Since we are performing an APPLY and not a JOIN, &lt;span style="font-style: italic;"&gt;A.Val &lt;/span&gt;is in scope and it works just fine.&lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-weight: bold;"&gt;Table Valued User Defined Functions&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
Note that the same rules apply when using &lt;a target="_blank" href="http://technet.microsoft.com/en-us/library/ms191165.aspx"&gt;Table-Valued User-Defined Functions&lt;/a&gt;:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;select A.*, B.X&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;from A&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;&lt;span style="font-weight: bold;"&gt;cross join&lt;/span&gt; &lt;span style="font-weight: bold;"&gt;dbo.UDF(A.Val) B&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
is not legal; once again, &lt;span style="font-style: italic;"&gt;A.Val&lt;/span&gt; is not in scope for the user-defined function.  The best we can do before SQL 2005 was to use a correlated sub-query:&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 A.*, (select X from dbo.UDF(A.Val)) X&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;from A&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
However, that is not logically equivalent; the UDF cannot return more than 1 row or it will result in an error, and wouldn't make logical sense anyway if it did.&lt;br /&gt;
&lt;br /&gt;
Starting with SQL 2005, we can now use CROSS APPLY and it will work fine:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;select A.*, b.X&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;from A&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New; font-weight: bold;"&gt;cross apply dbo.UDF(A.Val) b&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
So, that is one way to think of the difference between a JOIN and an APPLY; a JOIN combines two separate result sets, but APPLY is more of a loop that evaluates one result set over and over for each row in another.  This means that, in general, APPLY will be less efficient than a JOIN, just as, in general, correlated sub-queries are less efficient than derived tables.  (The optimizer, however, is generally quite good at optimized correlated sub-queries when possible.)&lt;br /&gt;
&lt;br /&gt;
So, why use CROSS APPLY instead of a correlated sub-query?  What's the advantage?  Actually, quite a lot -- it is much more powerful!&lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-weight: bold;"&gt;CROSS APPLY can return multiple rows &lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
Unlike correlate sub-queries, CROSS APPLY works with multiple rows.  This allows us to do things like "joining" a table to a function that parses a CSV column in that table into multiple rows:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;select A.ID, b.Val&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;from A&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New; font-weight: bold;"&gt;cross apply dbo.ParseCSV(A.CSV) b&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
When the ParseCSV() function returns multiple rows, it simply acts as if we have joined Table A to the function's return table, duplicating the rows in Table A for each row in the joined table.  This is not possible with a correlated sub-query, and will result in an error.  This is a quick and easy way to parse a table of data into multiple rows in an efficient "set-based" manner when the algorithm requires a complex User-Defined Function. (The CSV is not a great example, but other parsing routines are not as &lt;a href="http://weblogs.sqlteam.com/peterl/archive/2007/10/15/Cross-apply-in-SQL-Server-2000.aspx" target="_blank"&gt;easily accomplished via a JOIN to a numbers table&lt;/a&gt;.)&lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-weight: bold;"&gt;CROSS APPLY can return multiple columns&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
Again, in a correlated sub-query, we can only return a single value.  If we write a SQL statement that returns a running sum, we can use a correlated sub-query like this:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;select o.*, &lt;br /&gt;
  (select sum(Amount) from Order o &lt;br /&gt;
   where p.OrderDate &amp;lt;= o.OrderDate) as RunningSum&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;from Order o&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
However, what if we'd like to return an additional running sum of Orders based on some other criteria (e.g., for orders with the same "OrderCode")?  We'd need another correlated sub-query, greatly reducing the efficiency of our SELECT:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;select o.*, &lt;br /&gt;
  (select sum(Amount) from Order o &lt;br /&gt;
   where p.OrderDate &amp;lt;= o.OrderDate) as RunningSum,&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt; &lt;span style="font-weight: bold;"&gt; (select sum(Amount) from Order o &lt;/span&gt;&lt;br style="font-weight: bold;" /&gt;
&lt;span style="font-weight: bold;"&gt;    where p.OrderCode = o.OrderCode and p.OrderDate &amp;lt;= o.OrderDate) as SameCode&lt;/span&gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;from Order o&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
However,  we can easily re-write that using a single CROSS APPLY:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;select o.*, rs.RunningSum, rs.SameCode&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;from Order o&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;cross apply &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 &lt;br /&gt;
     sum(Amount) as RunningSum, &lt;br /&gt;
&lt;span style="font-weight: bold;"&gt;      sum(case when p.OrderCode = o.OrderCode then Amount else 0 end) as SameCode &lt;/span&gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;   from Order P &lt;br /&gt;
   where P.OrderDate &amp;lt;= O.OrderDate&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt; ) rs&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
So, we get the benefit of return multiple columns like a derived table, and we also get the ability to reference outer values in our SELECT to use in criteria and CASE statements.  Very, very powerful.&lt;br /&gt;
 &lt;br /&gt;
CROSS APPLY also allows us to quickly get columns from the "previous" row in a table quite easily:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;select o.*, prev.*&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;from Order o&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New; font-weight: bold;"&gt;cross apply &lt;/span&gt;&lt;br style="font-family: Courier New; font-weight: bold;" /&gt;
&lt;span style="font-family: Courier New; font-weight: bold;"&gt; (&lt;/span&gt;&lt;br style="font-family: Courier New; font-weight: bold;" /&gt;
&lt;span style="font-family: Courier New; font-weight: bold;"&gt;   select top 1 *&lt;/span&gt;&lt;br style="font-family: Courier New; font-weight: bold;" /&gt;
&lt;span style="font-family: Courier New; font-weight: bold;"&gt;   from Order P where P.OrderDate &amp;lt; O.OrderDate&lt;/span&gt;&lt;br style="font-family: Courier New; font-weight: bold;" /&gt;
&lt;span style="font-family: Courier New; font-weight: bold;"&gt;   order by OrderDate DESC&lt;/span&gt;&lt;br style="font-family: Courier New; font-weight: bold;" /&gt;
&lt;span style="font-family: Courier New; font-weight: bold;"&gt; ) prev&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
 &lt;br /&gt;
We can express the running total and counts as CROSS JOINS or INNER JOINS by reworking a correlated sub-query, but using TOP and referencing the outer SELECT is not easily done in that manner.  &lt;br /&gt;
&lt;br /&gt;
Note that the above CROSS APPLY will not return any orders without a previous order; we would use OUTER APPLY to ensure that all orders are returned even if no previous orders exist:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;select o.*, prev.*&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;from Order o&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New; font-weight: bold;"&gt;outer apply&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 top 1 *&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;   from Order P where P.OrderDate &amp;lt; O.OrderDate&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;   order by OrderDate DESC&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt; ) prev&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
We can also CROSS APPLY to a Table-Valued User Defined Function that returns exactly one row, but with multiple columns, to return separate pieces of data from a single function call.  For example, we can parse an email address into separate &lt;span style="font-style: italic;"&gt;username &lt;/span&gt;and &lt;span style="font-style: italic;"&gt;domain &lt;/span&gt;columns.  A few months back I wrote a &lt;a href="http://www.sqlteam.com/article/returning-complex-data-from-user-defined-functions-with-cross-apply" target="_blank"&gt;SQL Team article&lt;/a&gt; that discusses that concept.&lt;span style="font-weight: bold;"&gt;&lt;br /&gt;
&lt;br /&gt;
Summary&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
CROSS and OUTER APPLY are very powerful and can be very useful, but we must be careful to use them only when necessary; I am still testing the possibilities, but in general an APPLY will tend to be not as efficient as a JOIN.  In addition, APPLY is mostly demonstrated by applying table-valued user-defined functions, but it can be used with in-line SELECT statements as well.&lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-style: italic;"&gt;see also:&lt;br /&gt;
&lt;/span&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;a href="../../../../jeffs/archive/2007/10/18/sql-server-cross-apply.aspx" title="View Entry"&gt;Taking a look at CROSS APPLY&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="../../../../jeffs/archive/2007/07/12/60254.aspx" title="View Entry" id="ctl00_pageContent_Editor_Results_rprSelectionList_ctl08_HyperLink1"&gt;The "Nested WHERE-IN" SQL Anti-Pattern&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="../../../../jeffs/archive/2007/06/12/60230.aspx" title="View Entry" id="ctl00_pageContent_Editor_Results_rprSelectionList_ctl06_HyperLink1"&gt;Using GROUP BY to avoid self-joins&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="../../../../jeffs/archive/2007/05/14/60205.aspx" title="View Entry"&gt;Criteria on Outer Joined Tables&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="../../../../jeffs/archive/2007/04/19/Full-Outer-Joins.aspx" title="View Entry"&gt;Better Alternatives to a FULL OUTER JOIN&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="../../../../jeffs/archive/2007/04/03/Conditional-Joins.aspx" title="View Entry" id="ctl00_pageContent_Editor_Results_rprSelectionList_ctl10_HyperLink1"&gt;Conditional Joins in SQL Server&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="../../../../jeffs/archive/2006/06/19/10270.aspx" title="View Entry"&gt;How to JOIN Multiple Transactional Tables in SQL&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="../../../../jeffs/archive/2005/09/12/7755.aspx" title="View Entry" id="ctl00_pageContent_Editor_Results_rprSelectionList_ctl02_HyperLink1"&gt;The power of the Cross Join&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;img src="http://weblogs.sqlteam.com/jeffs/aggbug/60379.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Jeff Smith</dc:creator>
            <guid>http://weblogs.sqlteam.com/jeffs/archive/2007/10/18/sql-server-cross-apply.aspx</guid>
            <pubDate>Thu, 18 Oct 2007 21:01:20 GMT</pubDate>
            <wfw:comment>http://weblogs.sqlteam.com/jeffs/comments/60379.aspx</wfw:comment>
            <comments>http://weblogs.sqlteam.com/jeffs/archive/2007/10/18/sql-server-cross-apply.aspx#feedback</comments>
            <slash:comments>3</slash:comments>
            <wfw:commentRss>http://weblogs.sqlteam.com/jeffs/comments/commentRss/60379.aspx</wfw:commentRss>
            <trackback:ping>http://weblogs.sqlteam.com/jeffs/services/trackbacks/60379.aspx</trackback:ping>
        </item>
        <item>
            <title>SQL 2005 PIVOT Operator (link)</title>
            <link>http://weblogs.sqlteam.com/jeffs/archive/2007/07/12/60253.aspx</link>
            <description>There's two interesting posts over at the &lt;a target="_blank" href="http://blogs.msdn.com"&gt;MSDN blogs&lt;/a&gt; from &lt;a href="http://blogs.msdn.com/craigfr/"&gt;Craig Freedman&lt;/a&gt; about the new PIVOT operator in SQL 2005.  First, he gives a nice &lt;a href="http://blogs.msdn.com/craigfr/archive/2007/07/03/the-pivot-operator.aspx" target="_blank"&gt;overview&lt;/a&gt; of the operator and how to use it, and then he follows it up with a &lt;a href="http://blogs.msdn.com/craigfr/archive/2007/07/09/pivot-query-plans.aspx" target="_blank"&gt;performance analysis&lt;/a&gt; as well.  A good read, check it out.  &lt;br /&gt;
&lt;br /&gt;
I hope to cover both the PIVOT and also the UNPIVOT operators a little myself at some point in a future post.&lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-style: italic;"&gt;see also:&lt;br /&gt;
&lt;/span&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;a href="../../../../jeffs/archive/2003/10/24/363.aspx" title="View Entry"&gt;Keep those SQL Crosstabs flexible!&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="../../../../jeffs/archive/2005/05/11/5101.aspx" title="View Entry"&gt;.NET CrossTabs: Transforming a Normalized DataReader into a Pivoted DataTable&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="../../../../jeffs/archive/2005/05/12/5127.aspx" title="View Entry" id="ctl00_pageContent_Editor_Results_rprSelectionList_ctl04_HyperLink1"&gt;.NET CrossTabs versus SQL Server CrossTabs&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="../../../../jeffs/archive/2005/05/15/5175.aspx" title="View Entry"&gt;ASP and ADO Pivots -- Old School CrossTabs&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="../../../../jeffs/archive/2007/04/20/60182.aspx" title="View Entry" id="ctl00_pageContent_Editor_Results_rprSelectionList_ctl02_HyperLink1"&