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.

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:

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:

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:

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:

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.)