You get a bonus - 1 coin for daily activity. Now you have 1 coin

Comparing the Performance of SQL EXISTS, IN and JOIN

Lecture



Problem

I created a query that uses a subquery that needs to be compared to the main query. I want to know the best way to accomplish this task. Should I use the IN operator? EXISTS? Or maybe JOIN? I need to know which options will be valid for my use case and which one will perform best. I also need to prove it.

Solution

As with many situations in SQL Server, the answer depends on the circumstances. This tip will cover the pros and cons of each method and will use a repeatable methodology to determine which of the methods delivers the best performance.

Compare SQL Server EXISTS with IN and JOIN

All the demonstrations in this mini study will use records from a demo database. Your results may differ from the ones I obtained, but the methodology and the underlying principles of the solution will be the same.

The subquery that will be used will represent a list of the top three salespeople for a single quarter based on the number of invoices. For simplicity in the example queries, this subquery will be saved as a view. For a query as simple as this, there is probably no performance difference between using a view-based SQL, a CTE-based SQL, or a traditional subquery.

 Comparing the Performance of SQL EXISTS, IN and JOIN

IN SQL Code

The IN operator can be used to find rows in a query where one column can be matched against a value in a list of values. The list of values can be hard-coded as a comma-separated list, or it can come from a subquery, as in this example.

IN statements are easy to write and understand. The only drawback is that they can only compare a single column from the subquery to a single column from the main query. If it is necessary to compare 2 or more values, the IN operator cannot be used.

Below is a query that returns some invoices belonging to our top group of salespeople. Note that the subquery returns exactly one row. This is a requirement for using the IN operator. Also note that the query in parentheses is itself a fully functional query on its own. It can be selected and executed by itself.

 Comparing the Performance of SQL EXISTS, IN and JOIN

This information is output when this query is executed with the STATISTICS IO and STATISTICS TIME functions enabled, or with EXPLAIN.

This output will give us some performance metrics to compare with the other options. If you don’t know how to get this output, refer to the EXPLAIN or STATISTICS IO documentation.

Comparing the Performance of SQL EXISTS, IN and JOIN

EXISTS SQL Code

The EXISTS operator works similarly to the IN operator, except that it can be used to find rows where one or more columns from the query can be found in another dataset, usually a subquery. Hard-coding is not possible with EXISTS.

Below is the same query as above, except that IN has been replaced with EXISTS. The format of EXISTS and its subquery is somewhat different from that of IN. In this case, the subquery references the column I.SalespersonPersonID, which appears to be available to the subquery. For this reason, the subquery cannot be executed on its own and can only be executed in the context of the entire query. This can sometimes be difficult to understand.

Logically, you can think of this as running the subquery once for each row in the main query to determine whether the row exists. If the row exists when the subquery is executed, then the returned boolean value is true. Otherwise, it is false. The columns selected in the subquery do not matter, since the result depends only on the existence or absence of a row based on the FROM / JOIN / WHERE clauses in the subquery.

 Comparing the Performance of SQL EXISTS, IN and JOIN

Executing this query returns the following statistical output, which is virtually identical to the IN operator.

Comparing the Performance of SQL EXISTS, IN and JOIN

INNER JOIN SQL Code

A regular JOIN can be used to find matching values in a subquery. Like EXISTS, JOIN allows one or more columns to be used to find matches. Unlike EXISTS, JOIN is simpler to implement. The downside of JOIN is that if there are any duplicate rows in the subquery based on the JOIN predicate, then the main query will duplicate rows, which can lead to incorrect query conclusions. Both IN and EXISTS ignore duplicate values in the subquery. Take special precautions when joining tables this way. In this example, the view will not return duplicate SalespersonPersonID values, so this is a safe implementation of JOIN.

 Comparing the Performance of SQL EXISTS, IN and JOIN

Executing this query returns the following statistical output, which, again, is virtually identical to the IN and EXISTS versions of the query.

Comparing the Performance of SQL EXISTS, IN and JOIN

Why is all the statistics the same?

The statistics for each of these three options are virtually identical because the optimizer merges all 3 options into the same query plan. This can be verified by running all three queries together while viewing the actual execution plans. The screenshot below shows a single plan, but the same plan is displayed for each of the three query variants.

Comparing the Performance of SQL EXISTS, IN and JOIN

Each copy of the query plan shows a missing index recommendation. Implementing this recommendation and creating the index will change the query plans and performance statistics. Will it change them all the same way? Let’s find out. First, create the index, then re-run the 3 queries.

 Comparing the Performance of SQL EXISTS, IN and JOIN

Now run all 3 statements together again. Something interesting happens. All 3 plans changed compared to the versions before the index was created, but this time they are not identical. IN and EXISTS have the same new plan, but JOIN has a different plan.

The plan for IN and EXISTS used the new index twice and performed a seek on the People table.

Comparing the Performance of SQL EXISTS, IN and JOIN

This plan was produced for the JOIN version of the query. It used the new index twice, but performed a scan of the People table.

Comparing the Performance of SQL EXISTS, IN and JOIN

Checking the I/O and time statistics for the 3 queries shows identical statistics for the 2 queries with a shared plan, but improved statistics and execution time for the JOIN version. If this were a query being prepared for promotion to production, then JOIN would probably be the best option.

Comparing the Performance of SQL EXISTS, IN and JOIN

Conclusion

This query is an excellent example of the fact that, although the optimizer strives to treat all options the same way, it does not always do so. Using this performance-testing methodology, along with an understanding of the value and limitations of each query option, will allow a developer to make the best choice in each situation.

See also

  • [[b8656]]
  • [[b176]]

See also

Comments

To leave a comment

If you have any suggestion, idea, thanks or comment, feel free to write. We really value feedback and are glad to hear your opinion.
To reply

Lectures and tutorial on "Databases - Error detection methods in SQL application"

Terms: Databases - Error detection methods in SQL application