Blog Post

ISNULL around the predicate and SARGability

,

You may have heard of the word, SARGable. In brief, it’s a term derived from the phrase, Search ARGument able, and relates to SQL Server’s ability to seek through an index for a predicate. Non-sargable predicates could lead to poor query performance which is why it’s important to understand this. Sargability can be affected with the use of functions in the WHERE, ORDER BY, GROUP BY and HAVING clauses.

There are a number of articles that talk about sargability but we are going to focus just on the ISNULL function and it’s effect on sargability in this article. There is something quite interesting in store too. I will be running some queries against the StackOverflow database in order to present this.

Let’s start by running the following query

SELECT TOP 10 Id 
FROM Users
WHERE Age > 18

Before looking at the execution plan, I think it’s important to know the schema and the index available.

post64_UsersTableColumnspost64_IX_Users_Age_index

 

The execution plan looks as follows, just as you would expect.

post64_ExecutionPlan1

Now let’s run the following query

SELECT TOP 10 Id 
FROM Users
WHERE ISNULL(Age, 0) > 18

Note the ISNULL function on the column ‘Age’ in the WHERE clause.

Let’s look at the execution plan as well.

post64_ExecutionPlan2

We are now doing a scan on the non-clustered index instead of a seek, because SQL does not think the predicate is sargable any more. You might think “Why the non-clustered index?” Since the non-clustered index is the smallest index available to SQL Server that provides all the column it requires to run the query, it does not need to scan the clustered index.

So let’s experiment a bit here on.

So how many NULL values do I have in the Age column?

SELECT COUNT(*) 
FROM Users 
WHERE Age IS NULL

I get 3053086.

And what do my statistics look like:

DBCC SHOW_STATISTICS('[dbo].[Users]', [IX_Users_Age])

post64_Statitsics1

So could it be from the statistics that SQL knows we have NULL values and hence it doing a scan on the non-clustered index?

Let’s update all the NULL values and replace them with ‘0’ instead.

UPDATE Users
SET Age = 0 
WHERE Age IS NULL

Let’s also make sure that the statistics are updated.

post64_Statitsics2

Let’s re-run our query again and check out the execution plan.

post64_ExecutionPlan3

No change as you can see above.

I wonder if it’s the column definition – the column Age accepts NULL values.

Let’s change this column definition. To do this we would need to drop the index on Age and re-create it.

/****** Object:  Index [IX_Users_Age]     ******/DROP INDEX [IX_Users_Age] ON [dbo].[Users]
GO
ALTER TABLE Users
ALTER COLUMN Age int not null
GO
/****** Object:  Index [IX_Users_Age]     ******/CREATE NONCLUSTERED INDEX [IX_Users_Age] ON [dbo].[Users]
(
[Age] ASC
)

Time to re-run our query.

post64_ExecutionPlan4

Voila!

Back to seek… This is somewhat an “educated” behaviour from SQL Server.

TOP TIP: As mentioned right at the beginning, non-sargable queries will impact your query performance adversely. There might be scenarios which might lead you to think that it’s not true. However, in order to prevent your query from suffering this degradation in performance it’s best to keep your predicates sargable and avoid using functions in the WHERE, GROUP BY, ORDER BY and HAVING clauses.

Another important point to note is that if you didn’t have a suitable non-clustered index and SQL Server was scanning the clustered index because of a function on the predicate, you wouldn’t get any missing index details from the DMVs (Dynamic Management Views) to help you improve query performance.

The post ISNULL around the predicate and SARGability appeared first on SQLYSE.

Rate

You rated this post out of 5. Change rating

Share

Share

Rate

You rated this post out of 5. Change rating