<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>Joins/Relations</title>
        <link>http://weblogs.sqlteam.com/jeffs/category/248.aspx</link>
        <description>Anything involving joining or relating tables in SQL; includes join techniques, optimizations, etc.</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>On RIGHT OUTER JOINS ...</title>
            <link>http://weblogs.sqlteam.com/jeffs/archive/2008/02/13/on-right-outer-joins.aspx</link>
            <description>Because I feel pretty strongly about this and the entire focus of my blog is writing clear, clean and efficient SQL, I thought I'd repeat my response from a &lt;a href="http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=97137" target="_blank"&gt;SQLTeam forum question&lt;/a&gt; here.&lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-weight: bold;"&gt;smithje &lt;/span&gt;asks this, regarding OUTER JOINS:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px; font-style: italic;"&gt;Left/Right, does it matter. Is one better than the other? A few years ago consultant on a project for our company advised me to always write my queries to use Left joins. He had worked on the project to convert the original database application to MS SQL when Microsoft took it over. He claimed the design of the query engine handled Left joins more effeciently than right. I converted several queries that processed large datasets to Left join only and got quicker results. I have used Left exclusively since then. Has this concept ever been tested or written about?&lt;/div&gt;
&lt;br /&gt;
&lt;span style="font-weight: bold;"&gt;My response:&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
Hi -- they are technically the same, but it is always clearer to use LEFT OUTER JOINS. I strongly recommend to never use RIGHT OUTER JOINS.&lt;br /&gt;
&lt;br /&gt;
When you write a SQL statement, you should express your "base" in your FROM clause, and from there join to auxiliary tables. There is no SELECT statement that cannot be written in that manner, and it is a nice clear, clean way to organize your code. So, if you want ALL customers and ANY orders that match, I think we can all agree that it makes logical sense to express this as:&lt;br /&gt;
&lt;br /&gt;
SELECT ...&lt;br /&gt;
FROM customers&lt;br /&gt;
OUTER JOIN TO orders&lt;br /&gt;
&lt;br /&gt;
Clearly, we are primarily selecting customers as our "base", and including any Orders that may or may not exist.&lt;br /&gt;
&lt;br /&gt;
As a RIGHT OUTER JOIN, it becomes:&lt;br /&gt;
&lt;br /&gt;
SELECT&lt;br /&gt;
FROM Orders&lt;br /&gt;
OUTER JOIN TO Customers&lt;br /&gt;
&lt;br /&gt;
which doesn't make sense -- why are we selecting FROM Orders and joining TO Customers, when potentially we want to return Customers that don't have ANY orders?&lt;br /&gt;
&lt;br /&gt;
Anyway, it is rare to get good advice from a consultant, but it appears that you actually did! Avoid RIGHT JOINS, and stick with LEFT JOINS. If a right outer join seems required to make your query work, you should re-write it and change your FROM clause to make it cleaner, simpler and clearer.&lt;br /&gt;
&lt;br /&gt;
(by the same token, I strongly recommend to &lt;a href="http://weblogs.sqlteam.com/jeffs/archive/2007/04/19/Full-Outer-Joins.aspx"&gt;avoid FULL OUTER JOINS&lt;/a&gt; as well.)&lt;img src="http://weblogs.sqlteam.com/jeffs/aggbug/60508.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Jeff Smith</dc:creator>
            <guid>http://weblogs.sqlteam.com/jeffs/archive/2008/02/13/on-right-outer-joins.aspx</guid>
            <pubDate>Wed, 13 Feb 2008 16:22:25 GMT</pubDate>
            <wfw:comment>http://weblogs.sqlteam.com/jeffs/comments/60508.aspx</wfw:comment>
            <comments>http://weblogs.sqlteam.com/jeffs/archive/2008/02/13/on-right-outer-joins.aspx#feedback</comments>
            <slash:comments>6</slash:comments>
            <wfw:commentRss>http://weblogs.sqlteam.com/jeffs/comments/commentRss/60508.aspx</wfw:commentRss>
            <trackback:ping>http://weblogs.sqlteam.com/jeffs/services/trackbacks/60508.aspx</trackback:ping>
        </item>
        <item>
            <title>Rewriting correlated sub-queries with CASE expressions</title>
            <link>http://weblogs.sqlteam.com/jeffs/archive/2008/01/09/rewrite-correlated-sub-query-with-case-sql.aspx</link>
            <description>Here's a very common situation that is very easy to optimize and simplify, submitted via the &lt;a target="_blank" href="http://weblogs.sqlteam.com/jeffs/contact.aspx"&gt;mailbag&lt;/a&gt;:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;Nate writes:&lt;span style="font-style: italic;"&gt;&lt;br /&gt;
&lt;br /&gt;
Hey, I have a read a bunch of your stuff on your blog and you seem to&lt;/span&gt;&lt;br style="font-style: italic;" /&gt;
&lt;span style="font-style: italic;"&gt; be right on the money. I thought maybe you would be able to point me in&lt;/span&gt;&lt;br style="font-style: italic;" /&gt;
&lt;span style="font-style: italic;"&gt; the right direction and possibly address this issue on your blog so&lt;/span&gt;&lt;br style="font-style: italic;" /&gt;
&lt;span style="font-style: italic;"&gt; others could benefit from your understanding. &lt;/span&gt;&lt;br style="font-style: italic;" /&gt;
&lt;br style="font-style: italic;" /&gt;
&lt;span style="font-style: italic;"&gt;I have been searching for the best way to do what I think should be a&lt;/span&gt;&lt;br style="font-style: italic;" /&gt;
&lt;span style="font-style: italic;"&gt; simple task in SQL. I have a table full of call history events and I&lt;/span&gt;&lt;br style="font-style: italic;" /&gt;
&lt;span style="font-style: italic;"&gt; want to get a summary of some events for each calling party that matches&lt;/span&gt;&lt;br style="font-style: italic;" /&gt;
&lt;span style="font-style: italic;"&gt; the form "username(extension)". Using correlated subqueries I do the&lt;/span&gt;&lt;br style="font-style: italic;" /&gt;
&lt;span style="font-style: italic;"&gt; following.&lt;/span&gt;&lt;br style="font-style: italic;" /&gt;
&lt;br style="font-style: italic;" /&gt;
&lt;span style="font-style: italic;"&gt;SELECT calling_party,&lt;/span&gt;&lt;br style="font-style: italic;" /&gt;
&lt;br style="font-style: italic;" /&gt;
&lt;span style="font-style: italic;"&gt;(SELECT SUM(end_time-start_time) AS total_time FROM `event` ei WHERE&lt;/span&gt;&lt;br style="font-style: italic;" /&gt;
&lt;span style="font-style: italic;"&gt; event_type=4 AND ei.calling_party = eo.calling_party) AS&lt;/span&gt;&lt;br style="font-style: italic;" /&gt;
&lt;span style="font-style: italic;"&gt; total_talking_time, &lt;/span&gt;&lt;br style="font-style: italic;" /&gt;
&lt;br style="font-style: italic;" /&gt;
&lt;span style="font-style: italic;"&gt;(SELECT SUM(end_time-start_time) AS total_time FROM `event` ei WHERE&lt;/span&gt;&lt;br style="font-style: italic;" /&gt;
&lt;span style="font-style: italic;"&gt; event_type=7 AND ei.calling_party = eo.calling_party) AS&lt;/span&gt;&lt;br style="font-style: italic;" /&gt;
&lt;span style="font-style: italic;"&gt; total_ringing_time,&lt;/span&gt;&lt;br style="font-style: italic;" /&gt;
&lt;br style="font-style: italic;" /&gt;
&lt;span style="font-style: italic;"&gt;(SELECT MAX(end_time-start_time) AS total_time FROM `event` ei WHERE&lt;/span&gt;&lt;br style="font-style: italic;" /&gt;
&lt;span style="font-style: italic;"&gt; event_type=4 AND ei.calling_party = eo.calling_party) AS max_talking_time&lt;/span&gt;&lt;br style="font-style: italic;" /&gt;
&lt;br style="font-style: italic;" /&gt;
&lt;span style="font-style: italic;"&gt;FROM `event` eo WHERE calling_party LIKE '%(%)'&lt;/span&gt;&lt;br style="font-style: italic;" /&gt;
&lt;br style="font-style: italic;" /&gt;
&lt;span style="font-style: italic;"&gt;GROUP BY calling_party&lt;/span&gt;&lt;br style="font-style: italic;" /&gt;
&lt;br style="font-style: italic;" /&gt;
&lt;span style="font-style: italic;"&gt;This works, but ends up taking a really long time to run. I figured&lt;/span&gt;&lt;br style="font-style: italic;" /&gt;
&lt;span style="font-style: italic;"&gt; that each subquery was getting executed multiple times. I made a quick php&lt;/span&gt;&lt;br style="font-style: italic;" /&gt;
&lt;span style="font-style: italic;"&gt; script that returns the same information but uses multiple queries. I&lt;/span&gt;&lt;br style="font-style: italic;" /&gt;
&lt;span style="font-style: italic;"&gt; just query all of the "groups" (SELECT DISTINCT calling_party FROM&lt;/span&gt;&lt;br style="font-style: italic;" /&gt;
&lt;span style="font-style: italic;"&gt; `event` WHERE calling_party LIKE '%(%)') and then iterate over that&lt;/span&gt;&lt;br style="font-style: italic;" /&gt;
&lt;span style="font-style: italic;"&gt; recordset plugging the actual calling_party value into the where clause of the&lt;/span&gt;&lt;br style="font-style: italic;" /&gt;
&lt;span style="font-style: italic;"&gt; aggregate query. The output is identical, but this script runs in well&lt;/span&gt;&lt;br style="font-style: italic;" /&gt;
&lt;span style="font-style: italic;"&gt; under a second.&lt;/span&gt;&lt;br style="font-style: italic;" /&gt;
&lt;br style="font-style: italic;" /&gt;
&lt;span style="font-style: italic;"&gt;So the question is what is the correct way to do this using SQL? Any&lt;/span&gt;&lt;br style="font-style: italic;" /&gt;
&lt;span style="font-style: italic;"&gt; help would be really appreciated.&lt;/span&gt;&lt;br style="font-style: italic;" /&gt;
&lt;span style="font-style: italic;"&gt; &lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
Hi Nate --&lt;br /&gt;
&lt;br /&gt;
The script as written is making 4 different calls to the calling_party table, and we can reduce this to one call to the table by using CASE expressions to only aggregate the data we need for each column.  This is pretty much the standard "static cross-tab" technique to summarize data into multiple columns in a single SELECT.&lt;br /&gt;
&lt;br /&gt;
So, all we need to do is re-write your SELECT like this:&lt;br /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;select&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;  calling_party,&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;  sum(case when event_type=4 then end_time-start_time else 0 end) as total_talking_time,&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;  sum(case when event_type=7 then end_time-start_time else 0 end) as total_ringing_time,&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;  max(case when event_type=4 then end_time-start_time else 0 end) as max_talking_time&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;  event&lt;br /&gt;
where&lt;br /&gt;
&lt;/span&gt;    &lt;span style="font-family: Courier New;"&gt;calling_party LIKE '%(%)'&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;group by&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;  calling_party&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
That will return the same results as what you had written, only it should be much more efficient.  It is also much shorter.  Notice that the CASE expressions ensure that only the data with the specified event_type is including in each aggregate calculation, otherwise we aggregate a value of 0 which has no affect on the results.  You can also use a default value of NULL instead of 0 if you'd like, depending on your desired results.&lt;br /&gt;
&lt;br /&gt;
If you have more data in that table for event_types other than 4 and 7, and you only want to return calling_party rows that contain data with those event_types, then be sure to add&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;and event_type in (4,7)&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
to your WHERE clause, which will increase the efficiency even further.   However, adding that will return different results from your original SELECT (it returns all calling_groups regardless of event_type) so be sure that what you do meets the specifications you require.&lt;br /&gt;
&lt;br /&gt;
As always, be sure that you have proper indexes on all of your tables, and in this case it seems that event_type should certainly be indexed. &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 id="ctl00_pageContent_Editor_Results_rprSelectionList_ctl06_HyperLink1" title="View Entry" href="../../../../jeffs/archive/2007/11/13/sql-aggregate-totals.aspx"&gt;Some SELECTs will never return 0 rows -- regardless of the criteria&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a title="View Entry" href="../../../../jeffs/archive/2007/10/18/sql-server-cross-apply.aspx"&gt;Taking a look at CROSS APPLY&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a title="View Entry" href="../../../../jeffs/archive/2007/09/18/sql-conditional-where-clauses.aspx"&gt;Optimizing Conditional WHERE Clauses: Avoiding ORs and CASE Expressions&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a id="ctl00_pageContent_Editor_Results_rprSelectionList_ctl10_HyperLink1" title="View Entry" href="../../../../jeffs/archive/2007/06/12/60230.aspx"&gt;Using GROUP BY to avoid self-joins&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a title="View Entry" href="../../../../jeffs/archive/2007/05/14/60205.aspx"&gt;Criteria on Outer Joined Tables&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a title="View Entry" href="../../../../jeffs/archive/2007/04/19/Full-Outer-Joins.aspx"&gt;Better Alternatives to a FULL OUTER JOIN&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a id="ctl00_pageContent_Editor_Results_rprSelectionList_ctl02_HyperLink1" title="View Entry" href="../../../../jeffs/archive/2007/05/03/60195.aspx"&gt;In SQL, it's a Case Expression, *not* a Case Statement&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;img src="http://weblogs.sqlteam.com/jeffs/aggbug/60452.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Jeff Smith</dc:creator>
            <guid>http://weblogs.sqlteam.com/jeffs/archive/2008/01/09/rewrite-correlated-sub-query-with-case-sql.aspx</guid>
            <pubDate>Wed, 09 Jan 2008 13:42:16 GMT</pubDate>
            <wfw:comment>http://weblogs.sqlteam.com/jeffs/comments/60452.aspx</wfw:comment>
            <comments>http://weblogs.sqlteam.com/jeffs/archive/2008/01/09/rewrite-correlated-sub-query-with-case-sql.aspx#feedback</comments>
            <slash:comments>2</slash:comments>
            <wfw:commentRss>http://weblogs.sqlteam.com/jeffs/comments/commentRss/60452.aspx</wfw:commentRss>
            <trackback:ping>http://weblogs.sqlteam.com/jeffs/services/trackbacks/60452.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>Be Careful When Mixing INNER and OUTER Joins</title>
            <link>http://weblogs.sqlteam.com/jeffs/archive/2007/10/11/mixing-inner-outer-joins-sql.aspx</link>
            <description>I had previously written about the danger of &lt;a href="http://weblogs.sqlteam.com/jeffs/archive/2007/05/14/60205.aspx"&gt;Criteria on Outer Joins&lt;/a&gt;, but recently another situation popped up that occasionally causes confusion with OUTER JOINS that I thought I might address.  The issue is when you have multiple tables joined in a single SELECT, and you mix OUTER and INNER JOINS together.  The end result doesn't always seem to "work", and it can be tricky to understand exactly why and how to fix it without incurring additional unintended side effects.&lt;br /&gt;
&lt;br /&gt;
Consider the following schema and sample data:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;create table People&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;    PersonID int identity primary key,&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    PersonName varchar(20)&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 PetTypes&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;    PetTypeID int identity primary key,&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    PetType varchar(10)&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 Pets&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;    PetID int identity primary key,&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    PetTypeID int references PetTypes(PetTypeID) not null,&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    PetName varchar(10),&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    OwnerID int references People(PersonID) 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;&lt;/span&gt;&lt;span style="font-family: Courier New;"&gt;insert into People (PersonName)&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;select 'Fred Flintstone' union all&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;select 'Barney Rubble' union all&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;select 'George Jetson'&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 PetTypes (PetType)&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;select 'Dinosaur' union all&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;select 'Hopparoo'&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 Pets (PetTypeID, PetName, OwnerID)&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;select 1,'Dino',1 union all&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;select 2,'Hoppy',2&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
(note: I actually don't remember Hoppy at all from the &lt;a target="_blank" href="http://en.wikipedia.org/wiki/The_Flintstones"&gt;cartoon&lt;/a&gt;, but I read about him &lt;a href="http://en.wikipedia.org/wiki/Hoppy" target="_blank"&gt;here&lt;/a&gt;)&lt;br /&gt;
&lt;br /&gt;
Notice that all pets must have an owner, and all pets must have a PetType that indicates what they are.&lt;br /&gt;
&lt;br /&gt;
If we wish to return all People and their pets, if any, we would use an OUTER JOIN like this:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;select People.PersonName, Pets.PetName&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;from People&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New; font-weight: bold;"&gt;left outer join Pets on Pets.OwnerID = People.PersonID&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;PersonName           PetName    &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;Fred Flintstone      Dino&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;Barney Rubble        Hoppy&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;George Jetson        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;(3 row(s) affected)&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
We use the OUTER JOIN so that we still return George Jetson, even though he has no Pet.  Since there is no Pet for him, NULLS are returned for all columns in the Pets table for that row.  So far so good, this is exactly what we want, things are working great.&lt;br /&gt;
&lt;br /&gt;
Now, suppose we wish to indicate the PetType as well for each Pet.   All Pets must have a PetType since the column is not nullable, so it appears that we can simply add an INNER JOIN between Pets and PetTypes to our previous SELECT to include this information.  Let's give it a shot:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;select People.PersonName, Pets.PetName, PetTypes.PetType&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;from People&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;left outer join Pets on Pets.OwnerID = People.PersonID&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New; font-weight: bold;"&gt;inner join PetTypes on Pets.PetTypeID = PetTypes.PetTypeID&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;PersonName           PetName    PetType    &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;Fred Flintstone      Dino       Dinosaur&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;Barney Rubble        Hoppy      Hopparoo&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;
Where did George Jetson go?   We know that we can do an INNER JOIN between Pets and PetTypes since all Pets will always have a PetType match, and we did an OUTER JOIN from People to Pets so ensure that all People are returned regardless of having a pet or not.  On paper, then, it might seem that something isn't working correctly since the code seems solid.&lt;br /&gt;
&lt;br /&gt;
Well, we need to look a little more closely.  If we go back to our original OUTER JOIN (that included George in the results) and add the PetTypeID column from the Pets table to it, we get:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;select People.PersonName, Pets.PetName, Pets.PetTypeID&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;from People&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;left outer join Pets on Pets.OwnerID = People.PersonID&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;PersonName           PetName    PetTypeID   &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;Fred Flintstone      Dino       1&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;Barney Rubble        Hoppy      2&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New; font-weight: bold;"&gt;George Jetson        NULL       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;(3 row(s) affected)&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
Notice that the PetTypeID column is NULL for George.  SQL Server processes the joins in the order you specify, so it first gets the results from the OUTER JOIN between People and Pets, and then uses those results to INNER JOIN to the PetTypes table.  The NULL PetTypeID returned for George Jetson doesn't match any rows in the PetTypes table, and the INNER JOIN requires a match, so George is not returned in the results.&lt;br /&gt;
&lt;br /&gt;
So how do we fix this?  One approach commonly used, which should really be avoided, is to simply change the INNER JOIN between Pets and PetTypes to a LEFT OUTER JOIN as well:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;select People.PersonName, Pets.PetName, PetTypes.PetType&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;from People&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;left outer join Pets on Pets.OwnerID = People.PersonID&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New; font-weight: bold;"&gt;left outer join PetTypes on Pets.PetTypeID = PetTypes.PetTypeID&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;PersonName           PetName    PetType    &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;Fred Flintstone      Dino       Dinosaur&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;Barney Rubble        Hoppy      Hopparoo&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New; font-weight: bold;"&gt;George Jetson        NULL       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;(3 row(s) affected)&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
This appears to do the trick.  However, it is not the equivalent of our original intentions!  We initially wrote an INNER JOIN between Pets and PetTypes, since we do not want to return any Pets that do not have a corresponding PetType. In this case, it is not an issue because PetTypeID cannot be NULL for a Pet, but what if it could be?   The results will not be logically the same if we switch that INNER JOIN to an OUTER JOIN.&lt;br /&gt;
&lt;br /&gt;
For example, let's create a new version of the Pets table, that allows for NULL pet types:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;drop table Pets&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;create table Pets&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;    PetID int identity primary key,&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    PetTypeID int references PetTypes(PetTypeID),&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    PetName varchar(10),&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    OwnerID int references People(PersonID) 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;insert into Pets (PetTypeID, PetName, OwnerID)&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;select 1,'Dino',1 union all&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;select 2,'Hoppy',2 union all&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New; font-weight: bold;"&gt;select null,'Baby Puss',1&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
Notice now that we've included Fred's cat, &lt;a href="http://en.wikipedia.org/wiki/Baby_Puss_%28The_Flintstones%29" target="_blank"&gt;Baby Puss&lt;/a&gt;,  but we did not assign him a PetType.&lt;br /&gt;
&lt;br /&gt;
Let's now re-run the original OUTER JOIN/INNER JOIN statement that excluded George:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;select People.PersonName, Pets.PetName, PetTypes.PetType&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;from People&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;left outer join Pets on Pets.OwnerID = People.PersonID&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;inner join PetTypes on Pets.PetTypeID = PetTypes.PetTypeID&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;PersonName           PetName    PetType    &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;Fred Flintstone      Dino       Dinosaur&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;Barney Rubble        Hoppy      Hopparoo&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 same results are returned; George is excluded for reasons we discovered (and are trying to fix), and Baby Puss is missing because we explicitly asked for an INNER JOIN between Pets and PetTypes.  The only issue we should have, then, with the previous SELECT is that George is missing and we'd like to see him -- Baby Puss should be excluded by design.  (If that isn't clear, let me know) So, if we switch the second join to an OUTER JOIN in an attempt to fix things to get George returned, we now get:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;select People.PersonName, Pets.PetName, PetTypes.PetType&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;from People&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;left outer join Pets on Pets.OwnerID = People.PersonID&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New; font-weight: bold;"&gt;left outer join PetTypes on Pets.PetTypeID = PetTypes.PetTypeID&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;PersonName           PetName    PetType    &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;Fred Flintstone      Dino       Dinosaur&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New; font-weight: bold;"&gt;Fred Flintstone      Baby Puss  NULL&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;Barney Rubble        Hoppy      Hopparoo&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;George Jetson        NULL       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;
Notice that suddenly Baby Puss appears!  That is because we changed our join from Pets to PetTypes from an INNER to an OUTER.  So, just changing INNER JOINS to OUTER JOINS to "fix" this problem may have unintended side effects, and your query is no longer logically the same!  It may work in most cases, or in all cases depending on your constraints and relations, but it is important to understand that logically you have completely changed what you are asking for.&lt;br /&gt;
&lt;br /&gt;
The best solution is to use a derived table to encapsulate the INNER JOIN between Pets and PetTypes.  Then, we simply select FROM the People table and OUTER JOIN to the derived table.  This returns the results we are looking for:&lt;br /&gt;
&lt;br /&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;  People.PersonName, Pets.PetName, Pets.PetType&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;  People&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New; font-weight: bold;"&gt;left outer join &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 Pets.ownerID, Pets.PetName, PetTypes.PetType&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 Pets &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;  inner join PetTypes on Pets.PetTypeID = PetTypes.PetTypeID&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;" /&gt;
&lt;span style="font-family: Courier New;"&gt;  &lt;span style="font-weight: bold;"&gt;Pets&lt;/span&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;on&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;  Pets.OwnerID = People.PersonID&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;PersonName           PetName    PetType    &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;Fred Flintstone      Dino       Dinosaur&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;Barney Rubble        Hoppy      Hopparoo&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New; font-weight: bold;"&gt;George Jetson        NULL       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;(3 row(s) affected)&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
That's more like it!  Note that we could also use a Common Table Expression if you are using SQL 2005.&lt;br /&gt;
&lt;br /&gt;
An alternative option, which also works correctly, is to nest your join expression like this:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;select People.PersonName, Pets.PetName, PetTypes.PetType&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;from People&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;left outer join &lt;br /&gt;
&lt;span style="font-weight: bold;"&gt;   (Pets &lt;/span&gt;&lt;/span&gt;&lt;span style="font-family: Courier New;"&gt;&lt;span style="font-weight: bold;"&gt;inner join PetTypes on Pets.PetTypeID = PetTypes.PetTypeID) &lt;/span&gt;&lt;br /&gt;
on Pets.OwnerID = People.PersonID&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
That returns the correct results and is logically accurate, but I find it can be confusing to read and a little more difficult to maintain.  In general, I have found that most database programmers tend to avoid nested joins expressions.  It's up to you to decide which option you'd prefer to use.&lt;br /&gt;
&lt;br /&gt;
So, be careful when mixing INNER and OUTER joins; it can be tricky to hunt down unintended results and side effects.  If you use Derived Tables (or nested joins) the way you'd use parenthesis in a math or boolean equation to express your logical order of precedence, you can make your code cleaner and more readable and you can ensure that you get back the exact results you intended. &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 title="View Entry" href="../../../../jeffs/archive/2007/10/18/sql-server-cross-apply.aspx"&gt;Taking a look at CROSS APPLY&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a id="ctl00_pageContent_Editor_Results_rprSelectionList_ctl08_HyperLink1" title="View Entry" href="../../../../jeffs/archive/2007/07/12/60254.aspx"&gt;The "Nested WHERE-IN" SQL Anti-Pattern&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a id="ctl00_pageContent_Editor_Results_rprSelectionList_ctl06_HyperLink1" title="View Entry" href="../../../../jeffs/archive/2007/06/12/60230.aspx"&gt;Using GROUP BY to avoid self-joins&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a title="View Entry" href="../../../../jeffs/archive/2007/05/14/60205.aspx"&gt;Criteria on Outer Joined Tables&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a title="View Entry" href="../../../../jeffs/archive/2007/04/19/Full-Outer-Joins.aspx"&gt;Better Alternatives to a FULL OUTER JOIN&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a id="ctl00_pageContent_Editor_Results_rprSelectionList_ctl10_HyperLink1" title="View Entry" href="../../../../jeffs/archive/2007/04/03/Conditional-Joins.aspx"&gt;Conditional Joins in SQL Server&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a title="View Entry" href="../../../../jeffs/archive/2006/06/19/10270.aspx"&gt;How to JOIN Multiple Transactional Tables in SQL&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a id="ctl00_pageContent_Editor_Results_rprSelectionList_ctl02_HyperLink1" title="View Entry" href="../../../../jeffs/archive/2005/09/12/7755.aspx"&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/60369.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Jeff Smith</dc:creator>
            <guid>http://weblogs.sqlteam.com/jeffs/archive/2007/10/11/mixing-inner-outer-joins-sql.aspx</guid>
            <pubDate>Thu, 11 Oct 2007 18:39:49 GMT</pubDate>
            <wfw:comment>http://weblogs.sqlteam.com/jeffs/comments/60369.aspx</wfw:comment>
            <comments>http://weblogs.sqlteam.com/jeffs/archive/2007/10/11/mixing-inner-outer-joins-sql.aspx#feedback</comments>
            <slash:comments>23</slash:comments>
            <wfw:commentRss>http://weblogs.sqlteam.com/jeffs/comments/commentRss/60369.aspx</wfw:commentRss>
            <trackback:ping>http://weblogs.sqlteam.com/jeffs/services/trackbacks/60369.aspx</trackback:ping>
        </item>
        <item>
            <title>Creating CSV strings in SQL: Should Concatenation and Formatting Be Done at the Database Layer?</title>
            <link>http://weblogs.sqlteam.com/jeffs/archive/2007/10/09/csv-strings-database-or-presentation.aspx</link>
            <description>A question I see very often in the SQLTeam forums is how to return data in a summarized form by concatenating a list of values into a single CSV column.  This can be done fairly easily in T-SQL, but as the formatting and concatenation requirements becomes more elaborate, be sure to ask yourself: Am I forcing presentation code into the database layer?  &lt;a href="http://weblogs.sqlteam.com/jeffs/archive/2007/10/09/csv-strings-database-or-presentation.aspx"&gt;read more...&lt;/a&gt;&lt;img src="http://weblogs.sqlteam.com/jeffs/aggbug/60359.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Jeff Smith</dc:creator>
            <guid>http://weblogs.sqlteam.com/jeffs/archive/2007/10/09/csv-strings-database-or-presentation.aspx</guid>
            <pubDate>Tue, 09 Oct 2007 19:02:57 GMT</pubDate>
            <wfw:comment>http://weblogs.sqlteam.com/jeffs/comments/60359.aspx</wfw:comment>
            <comments>http://weblogs.sqlteam.com/jeffs/archive/2007/10/09/csv-strings-database-or-presentation.aspx#feedback</comments>
            <slash:comments>18</slash:comments>
            <wfw:commentRss>http://weblogs.sqlteam.com/jeffs/comments/commentRss/60359.aspx</wfw:commentRss>
            <trackback:ping>http://weblogs.sqlteam.com/jeffs/services/trackbacks/60359.aspx</trackback:ping>
        </item>
        <item>
            <title>"Nested WHERE-IN" Anti-Pattern Follow-up; More on Derived Tables (sub-queries)</title>
            <link>http://weblogs.sqlteam.com/jeffs/archive/2007/07/13/60255.aspx</link>
            <description>A quick follow up to the "Nested WHERE-IN" anti-pattern post from yesterday ...  If you didn't get a chance, be sure to read the comments from that post as well, there are some great points in there so far. &lt;a href="http://weblogs.sqlteam.com/jeffs/archive/2007/07/13/60255.aspx"&gt;read more...&lt;/a&gt;&lt;img src="http://weblogs.sqlteam.com/jeffs/aggbug/60255.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Jeff Smith</dc:creator>
            <guid>http://weblogs.sqlteam.com/jeffs/archive/2007/07/13/60255.aspx</guid>
            <pubDate>Fri, 13 Jul 2007 13:33:54 GMT</pubDate>
            <wfw:comment>http://weblogs.sqlteam.com/jeffs/comments/60255.aspx</wfw:comment>
            <comments>http://weblogs.sqlteam.com/jeffs/archive/2007/07/13/60255.aspx#feedback</comments>
            <slash:comments>3</slash:comments>
            <wfw:commentRss>http://weblogs.sqlteam.com/jeffs/comments/commentRss/60255.aspx</wfw:commentRss>
            <trackback:ping>http://weblogs.sqlteam.com/jeffs/services/trackbacks/60255.aspx</trackback:ping>
        </item>
        <item>
            <title>The "Nested WHERE-IN" SQL Anti-Pattern</title>
            <link>http://weblogs.sqlteam.com/jeffs/archive/2007/07/12/60254.aspx</link>
            <description>There's a fascinating technique that I see many beginners use when writing SQL statements, and I call it the "Nested WHERE-IN" anti-pattern.  It is, unfortunately, a common SQL technique used to avoid JOINS at all costs.   &lt;a href="http://weblogs.sqlteam.com/jeffs/archive/2007/07/12/60254.aspx"&gt;read more...&lt;/a&gt;&lt;img src="http://weblogs.sqlteam.com/jeffs/aggbug/60254.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Jeff Smith</dc:creator>
            <guid>http://weblogs.sqlteam.com/jeffs/archive/2007/07/12/60254.aspx</guid>
            <pubDate>Thu, 12 Jul 2007 16:50:44 GMT</pubDate>
            <wfw:comment>http://weblogs.sqlteam.com/jeffs/comments/60254.aspx</wfw:comment>
            <comments>http://weblogs.sqlteam.com/jeffs/archive/2007/07/12/60254.aspx#feedback</comments>
            <slash:comments>18</slash:comments>
            <wfw:commentRss>http://weblogs.sqlteam.com/jeffs/comments/commentRss/60254.aspx</wfw:commentRss>
            <trackback:ping>http://weblogs.sqlteam.com/jeffs/services/trackbacks/60254.aspx</trackback:ping>
        </item>
        <item>
            <title>Using GROUP BY to avoid self-joins</title>
            <link>http://weblogs.sqlteam.com/jeffs/archive/2007/06/12/60230.aspx</link>
            <description>Sometimes, it appears that a necessary solution to common SQL problems is to join a table to itself.   While self-joins do indeed have their place, and can be very powerful and useful, often times there is a much easier and more efficient way to get the results you need when querying a single table.
&lt;br&gt;&lt;br&gt;&lt;a href="http://weblogs.sqlteam.com/jeffs/archive/2007/06/12/60230.aspx"&gt;read more...&lt;/a&gt;&lt;img src="http://weblogs.sqlteam.com/jeffs/aggbug/60230.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Jeff Smith</dc:creator>
            <guid>http://weblogs.sqlteam.com/jeffs/archive/2007/06/12/60230.aspx</guid>
            <pubDate>Tue, 12 Jun 2007 15:35:33 GMT</pubDate>
            <wfw:comment>http://weblogs.sqlteam.com/jeffs/comments/60230.aspx</wfw:comment>
            <comments>http://weblogs.sqlteam.com/jeffs/archive/2007/06/12/60230.aspx#feedback</comments>
            <slash:comments>3</slash:comments>
            <wfw:commentRss>http://weblogs.sqlteam.com/jeffs/comments/commentRss/60230.aspx</wfw:commentRss>
            <trackback:ping>http://weblogs.sqlteam.com/jeffs/services/trackbacks/60230.aspx</trackback:ping>
        </item>
        <item>
            <title>Criteria on Outer Joined Tables</title>
            <link>http://weblogs.sqlteam.com/jeffs/archive/2007/05/14/60205.aspx</link>
            <description>When using an OUTER JOIN, you should put criteria on the outer table in the join condition, not in the WHERE clause.  However, I often see a "workaround" to avoid this simple and solid rule, which might seem to work but actually doesn't.  Since it is hard to explain why over and over in forum posts, I thought it might be helpful to address that here once and for all with an example.&lt;br&gt;&lt;br&gt;&lt;a href="http://weblogs.sqlteam.com/jeffs/archive/2007/05/14/60205.aspx"&gt;read more...&lt;/a&gt;&lt;img src="http://weblogs.sqlteam.com/jeffs/aggbug/60205.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Jeff Smith</dc:creator>
            <guid>http://weblogs.sqlteam.com/jeffs/archive/2007/05/14/60205.aspx</guid>
            <pubDate>Mon, 14 May 2007 14:12:33 GMT</pubDate>
            <comments>http://weblogs.sqlteam.com/jeffs/archive/2007/05/14/60205.aspx#feedback</comments>
            <slash:comments>13</slash:comments>
            <wfw:commentRss>http://weblogs.sqlteam.com/jeffs/comments/commentRss/60205.aspx</wfw:commentRss>
            <trackback:ping>http://weblogs.sqlteam.com/jeffs/services/trackbacks/60205.aspx</trackback:ping>
        </item>
        <item>
            <title>Thinking Set-Based .... or not?</title>
            <link>http://weblogs.sqlteam.com/jeffs/archive/2007/04/30/60192.aspx</link>
            <description>So, I hear you're a "set-based SQL master" !  As Yoda once said, you've "unlearned what you have learned". You've trained yourself to attack your database code not from a procedural, step-by-step angle, but rather from the set-based "do it all at once" approach.  However, don't completely forget the most important skill that you learned in the procedural world!&lt;br&gt;&lt;br&gt;&lt;a href="http://weblogs.sqlteam.com/jeffs/archive/2007/04/30/60192.aspx"&gt;read more...&lt;/a&gt;&lt;img src="http://weblogs.sqlteam.com/jeffs/aggbug/60192.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Jeff Smith</dc:creator>
            <guid>http://weblogs.sqlteam.com/jeffs/archive/2007/04/30/60192.aspx</guid>
            <pubDate>Mon, 30 Apr 2007 17:03:11 GMT</pubDate>
            <comments>http://weblogs.sqlteam.com/jeffs/archive/2007/04/30/60192.aspx#feedback</comments>
            <slash:comments>18</slash:comments>
            <wfw:commentRss>http://weblogs.sqlteam.com/jeffs/comments/commentRss/60192.aspx</wfw:commentRss>
            <trackback:ping>http://weblogs.sqlteam.com/jeffs/services/trackbacks/60192.aspx</trackback:ping>
        </item>
    </channel>
</rss>