Applying Functions on WHERE Clause Columns might Cause Serious Performance Problems

Any function such as collate, convert, substring etc. applied on a column might make impossible for SQL Server to use any index on this column. This because SQL Server query optimizer considers the column after the function as a new column, because the column values are not stored in the index pages as returned by the function. For these reasons, it is not possible to use indexes on these columns.

This is of course is not only for WHERE clause columns. Join, order, group by or having clause may have same problem. However functions in the select list does not affect index selection.

Some common problematic functions are :

  • COLLATE
  • CONVERT
  • SUBSTRING
  • LEFT
  • LTRIM
  • RTRIM
  • User defined functions

Ok now lets make some demos to see this performance problem.

/*

Turgay Sahtiyan - Microsoft SQL Server Premier Field Engineer

Blog : www.turgaysahtiyan.com

Twitter : @turgaysahtiyan

*/

--Applying Functions on Where Clause Columns

--Create a work database with Latin1_General_CI_AS Collation

Create Database DBCollate COLLATE Latin1_General_CI_AS

GO

Use DBCollate

GO

--Create a work table

create table tbl1(a varchar(37), b char(400))

GO

--Populate it with 10.000 records

declare @i int=0

declare @a char(37)

while @i<10000 begin

  select @a=CAST(NEWID() as CHAR(37))

  insert tbl1

    select @a,REPLICATE('b',400)

  set @i=@i+1

end

--Create a clustered index on column a

Create Clustered Index CX_1 on tbl1(a)

use DBCollate

GO

/*****************************************

--Sample 1 : Collate

*****************************************/

--Compare the query plans of the below queries

--CTRL+M

--Query 1 - index seek

select * from tbl1

where a = 'F0166605-4683-44AB-A859-0A98FFD337B9'

--Query 2 - index scan

select * from tbl1

where a = 'F0166605-4683-44AB-A859-0A98FFD337B9' COLLATE TURKISH_CI_AS

/*****************************************

Sample 2 : LTRIM - RTRIM

*****************************************/

--This query normally does index seek

select * from tbl1

where a ='F0166605-4683-44AB-A859-0A98FFD337B9'

--LTRIM : Index Scan

select * from tbl1

where LTRIM(a) ='F0166605-4683-44AB-A859-0A98FFD337B9'

--RTRIM : Index Scan

select * from tbl1

where RTRIM(a) ='F0166605-4683-44AB-A859-0A98FFD337B9'

/*****************************************

Sample 3 : Substring - Left

*****************************************/

--Substring : Index Scan

select * from tbl1

where substring(a,1,1) ='F'

--Substring : Index Scan

select * from tbl1

where left(a,1) ='F'

--Below query is exactly same with the substring one.

--But this guy does index seek

select * from tbl1

where a like 'F%'

--drop work database

use master

go

drop database DBCollate

Comments

  • Anonymous
    September 15, 2013
    Thanks you for this excellent article. A tiny reproach : why have you not provided the query plans ? It would be useful especially to discover the full scan of all the pages for some indexes. Maybe , it is only because it would need too much room in your post ( an understandable reason ) I will have a look at your other posts since the beginning. I hope they will be as good as this one.

  • Anonymous
    September 16, 2013
    Thanks for your comment Papy As you stated, pictures need too much space and make the post unreadable (imo) And also reader can copy and try it on his environment easily. That's why I normally don't prefer to use pics