Add APPLY to Your TSQL Tool Belt

Every once in a while I stumble across some SQL keyword that I didn’t really know about, but is extremely useful.

The other day I came across APPLY, or rather CROSS APPLY.

After reading through documentation on how it works and articles about it, I had a bit of trouble understanding it because I couldn’t really find a simple explanation.

criss cross 1256682 thumb Add APPLY to Your TSQL Tool Belt

I am going to try to explain it as simply as possible so you can start using it right away.

How CROSS APPLY works

The basic idea behind CROSS APPLY is to allow you to join two sets of data together.

If you understand how INNER JOIN works, you already understand CROSS APPLY.

The only difference is CROSS APPLY also allows you to join in a set of data in which that set of data is created or dependent on each row in the first set.

So basically what that means is that for a normal join you would, for example, join two tables that shared a common key.

I could join my customer table to my orders table to see all the orders for a particular customer like so:

[sourcecode language=”sql” padlinenumbers=”true”] SELECT *
FROM
orders o
JOIN customers c
ON o.customerid = c.customerid
WHERE
c.companyname = ‘Around the Horn’
[/sourcecode]

Notice how the join is operating on a key that exists independently in each table.

We could rewrite this to be exactly the same using the CROSS APPLY syntax instead like so:

[sourcecode language=”sql”] SELECT *
FROM
orders o
CROSS APPLY (
SELECT * FROM
customers c
WHERE
o.customerid = c.customerid) AS c
WHERE
c.companyname = ‘Around the Horn’
[/sourcecode]

We can prove these results sets are exactly the same by using EXCEPT to make sure there are no rows in one set that aren’t in the other and then flipping it, like so:

[sourcecode language=”sql”] SELECT *
FROM
orders o
CROSS APPLY (SELECT *
FROM
customers c
WHERE
o.customerid = c.customerid) AS c
WHERE
c.companyname = ‘Around the Horn’

EXCEPT
SELECT *
FROM
orders o
JOIN customers c
ON o.customerid = c.customerid
WHERE
c.companyname = ‘Around the Horn’
[/sourcecode]

Just run this exact query again swapping the SQL above the EXCEPT with the SQL below and make sure it has no results as well.  If both of those queries have no results, then you know the results from each query are the same since EXCEPT will show any results that are in the top query but not in the bottom one.

So when is CROSS APPLY useful?

Remember how I said it can do more than a simple join?  Joins are restricted to only joining two sets of data that could be queries independently of each other.

What if I said give me the three most recent orders for each customer?

Take a minute and think about how you would write that query.  Go ahead and try to do it.  I’ll wait.

There are a few ways to do it without using CROSS APPLY, but none of them are really very easy or perform very well.

Using CROSS APPLY it is simple though:

[sourcecode language=”sql”] SELECT *
FROM
customers c
CROSS APPLY (
SELECT TOP 3 o.orderdate
, o.shipcity
FROM
orders o
WHERE
o.customerid = c.customerId
order by o.orderdate desc
) as top3;
[/sourcecode]

So CROSS APPLY is useful whenever you have some data that you would want to be able to join against, but are forced to do some kind of sub-query instead because the data you are trying to join is not going to map well against a single key.

The other instance in which CROSS APPLY will be useful is when you are  doing a sub-select that has more than one value you would like to use in your final query.

For example if you were sub-selecting from an Order Details table to match up order ids that had a Quantity greater than 5, that sub-select would need to return exactly one column in order for you to use it in your where clause.  If you wanted to use other columns from the sub-select, you would have to do another sub-select for each of these columns.

If you first try to rewrite the sub-select as a JOIN and find that you can’t, you may be able to write it as a CROSS APPLY.

How to know when to use CROSS APPLY

There isn’t a good solid rule you can use to identify when you should use a CROSS APPLY but having the knowledge of CROSS APPLY and how it works can help you when you are trying to tune queries and you are having a difficult time constructing one.  It is another option you can try.

Here are some general guidelines of times when you might want to use CROSS APPLY:

  • A query where the result set you want to JOIN against is in some way related to the data in the first set.  (Example: one column in the first table tells you how many rows in the 2nd table to get)
  • A query where you are doing a sub-query, but need more than one value from the sub-query
  • Anywhere you are using a Common Table Expression (CTE) could possibly be rewritten as a CROSS APPLY
  • A query that has a large set of data it is joining against and then filtering out.  You can change it to a CROSS APPLY that does the filter in the CROSS APPLY statement.
  • Any time you are trying to join against a table function (this is actually what CROSS APPLY was created for.)

As always, you can subscribe to this RSS feed to follow my posts on Making the Complex Simple.  Feel free to check out ElegantCode.com where I post about the topic of writing elegant code about once a week.  Also, you can follow me on twitter here.

  • http://twitter.com/jason_grundy Jason Grundy (@jason_grundy)

    An alternative approach:

    SELECT
    *
    FROM
    (
    SELECT
    c.*,
    o.orderdate,
    o.shipcity,
    rank() over (partition by c.id order by o.orderdate desc) orderdate_rank
    FROM
    customers c
    inner join order o on c.customerid = o.customerid
    )
    WHERE
    orderdate_rank <= 3

    • http://simpleprogrammer.com jsonmez

      Yes, that is the alternative using PARTITION. Problem with that approach is that it is much slower and harder to understand. If you do a query plan on both approaches you’ll see what I mean.

  • http://gravatar.com/configurator configurator

    I wonder how I never heard of this. Very useful. If I understand correctly, this is like SelectMany, right?

    Is this a relatively commonly-supported feature?

    • http://simpleprogrammer.com jsonmez

      Hmm, I never thought of it that way. I suppose it is similar to selectmany from the idea that you are flattening out data, but I don’t think it parallels it functionality-wise.
      It is a SQL feature for MS SQL Server. Not sure if it is supported anywhere else.

  • Pingback: Dew Drop – September 23, 2011 | Alvin Ashcraft's Morning Dew()

  • http://lukaseder.wordpress.com lukaseder

    That’s nice and simple explanation of this useful T-SQL clause. Too bad, these useful little SQL extensions are often only available in one dialect.

    Btw: There’s also a nice explanation of the difference between CROSS APPLY and INNER JOIN on this Stack Overflow question:

    http://stackoverflow.com/questions/1139160/when-should-i-use-cross-apply-over-inner-join