Back to Insights
GuideJune 10, 20268 min read

SQL Server performance insights – OR vs UNION ALL is not always what it seems

Learn how UNION ALL can dramatically outperform OR operators in specific scenarios, potentially reducing resource consumption by up to 99x.

Jani K. Savolainen

Jani K. Savolainen

Founder & CTO, SQL Governor

This blogpost continues my series of blogposts in the field of Microsoft SQL Server performance optimization. Last time we learned how to build the best performing composite indexes from logical hierarchies. In this blogpost, I will show you how in the T-SQL world, OR vs UNION ALL is not always what it seems. You may be surprised!

Setting the scene

In my last blogpost, I had created a Fact table (F_DATABASE_STATISTICS) and a referred dimension table (D_DATABASE) in a star schema on SQL Server 2022. For this exercise, I have set max degree of parallelism to 6 and cost threshold for parallelism as low as 5.

Star schema with D_DATABASE dimension and F_DATABASE_STATISTICS fact table

From our earlier exercise, in addition to the clustered identity id primary key, we have the following indexes in the D_DATABASE table:

List of indexes in the D_DATABASE table

And the column cardinality is as follows:

Table showing column cardinality of the D_DATABASE table

The OR approach

Let's build a query that fetches the average CPU usage per database for several instances at once, using an OR operator to combine the different search conditions in the WHERE clause:

DBCC DROPCLEANBUFFERS;
SET STATISTICS TIME ON;

SELECT
     [DATABASE_NAME],
     AVG(PK_DATABASE) AS RANDOM_METRIC
FROM
     D_DATABASE
WHERE
     [INSTANCE_NAME] = 'MYINSTANCE1' OR
     [INSTANCE_NAME] = 'MYINSTANCE2' OR
     [INSTANCE_NAME] = 'MYINSTANCE3' OR
     [INSTANCE_NAME] = 'MYINSTANCE4' OR
     [INSTANCE_NAME] = 'MYINSTANCE5' OR
     [INSTANCE_NAME] = 'MYINSTANCE6'
GROUP BY
     [DATABASE_NAME];

Now let's compare the actual query plans of the OR query and the equivalent UNION ALL query:

Comparison of the OR query plan versus the UNION ALL query plan

The judgement is clear: the OR query seems to consume about 99 times more resources than the UNION ALL query! The Filter operator appears to split the OR query's WHERE clause into multiple threads, one for each OR part of the query.

OR query execution plan showing the Filter operator spreading work across threads

The UNION ALL approach

Now let's rewrite the same logic using UNION ALL, giving each instance its own SELECT that the engine can seek and aggregate independently:

SELECT
     [DATABASE_NAME],
     AVG(PK_DATABASE) AS RANDOM_METRIC
FROM
     D_DATABASE
WHERE
     [INSTANCE_NAME] = 'MYINSTANCE1'
GROUP BY
     [DATABASE_NAME]

UNION ALL

SELECT
     [DATABASE_NAME],
     AVG(PK_DATABASE) AS RANDOM_METRIC
FROM
     D_DATABASE
WHERE
     [INSTANCE_NAME] = 'MYINSTANCE2'
GROUP BY
     [DATABASE_NAME]

-- ... repeated for each instance ...;
SQL Server Execution Times:
CPU time = 0 ms, elapsed time = 106 ms.

Well, that was fast! (best out of 5 executions) Here we can see the power of parallelization in the query execution plan:

UNION ALL query execution plan showing efficient parallelized index seeks

What happens to the CPU?

For a curiosity, let's see what happens to the CPU with these two queries. This is an excerpt from the OR query's CPU footprint with 50 iterations — the OR query duration for 50 iterations was 59 seconds:

CPU utilization chart for the OR query showing very high usage across many threads

And here is the UNION ALL query's CPU footprint with 50 iterations — what a difference! The UNION ALL query execution time for 50 iterations dropped by about 30x, into just 2 seconds:

CPU utilization chart for the UNION ALL query showing low usage

As can be seen, the OR query spreads the query across a multitude of threads and consumes dramatically more CPU than the UNION ALL query.

Performance comparison results

Here are the SQL Server performance results for a single query:

Query typeExecution time (ms)CPU time (ms)Level of optimization
Thick index with several OR operators10824359
Thick index with several UNION ALL clauses1060~10x faster single query, no significant CPU usage at all
SQL Server execution time and CPU time results for OR versus UNION ALL

Lessons learned

Today we learned that OR vs UNION ALL performance is not always what it seems: it depends. UNION ALL can make a huge difference compared to the OR operator in query performance and overall server resource usage. With this said, it is still good to acknowledge that too many simultaneous UNION ALL clauses may produce context switching and CX_PACKET waits on a larger scale, so use it wisely.

The theory is only half of the meal served. Stay curious and pay attention to details. In the realm of SQL Server, never believe it before you have seen it.

Jani K. Savolainen

Jani K. Savolainen

Founder & CTO, SQL Governor

Want to optimize your SQL Server estate?

See exactly how many cores your workloads need and what the gap is worth. Book a demo with our team.

Book a demo