SQL SERVER INDEX (Part 5): Function-based Indexes
WHERE
clause that filters rows based on the values of func(C). func() is a function. In this case, the optimizer can make use of the index, but it cannot seek on the index.Problem
ON [dbo].[Posts] ( [creationdate] ASC )
include (id, title, lasteditdate, score)
SELECT id,
title,
lasteditdate,
score
FROM dbo.Posts
WHERE Year(creationdate) = 2008
Here, the optimizer performs an index scan rather than an index seek. Logical reads 28573.
ALTER TABLE dbo.Posts
ADD CreationYear AS Year(CreationDate)
CREATE NONCLUSTERED INDEX IX_Year_CreationDate
ON dbo.Posts(CreationYear)
include (id, title, lasteditdate, score)
Rerun the query. The optimizer performs an index seek, locating the rows where YEAR(CreationDate) = 2008
and scanning the qualifying rows. Logical reads 1670, significantly better than 28,573 reads.
When running the query, I can still keep the YEAR(CreationDate) and the optimizer still knows to use the computed column index.

Small tweak to the problem
title,
lasteditdate,
score,
answercount
FROM dbo.Posts
WHERE Year(CreationDate) = 2008
Running this modified query, the optimizer now scans the clustered index instead. Logical reads 800,000.
This is because AnswerCount was not included in the IX_Year_CreationDate index.
If we use IX_Year_CreationDate index, the optimizer needs key lookups to get the AnswerCount value for each record whose Creation Year is 2008.
However, because there are 280k rows whose Creation Year is 2008, 280k key lookups are required.
In this case, a clustered index scan is more efficient, which is why the optimizer chooses it.
Once again, we see that lookup operations are highly sensitive to cardinality estimates!
The solution is to include
AnswerCount
column in the IX_Year_CreationDate
index. ON dbo.posts
(
creationyear
)
include
(
id,
title,
lasteditdate,
score,
answercount
)
Alternative solution:
Function-based index is one way to solve, but in many situations, we can rewrite the query so that the index uses the original columns instead.Clean up
DROP INDEX [IX_Year_CreationDate] ON [dbo].[Posts];
Comments
Post a Comment