About Me

My photo
Mumbai, Maharastra, India
He has more than 7.6 years of experience in the software development. He has spent most of the times in web/desktop application development. He has sound knowledge in various database concepts. You can reach him at viki.keshari@gmail.com https://www.linkedin.com/in/vikrammahapatra/ https://twitter.com/VikramMahapatra http://www.facebook.com/viki.keshari

Search This Blog

Showing posts with label SQL 2008 feature. Show all posts
Showing posts with label SQL 2008 feature. Show all posts

Wednesday, July 30, 2014

Work Around for Filtered Index Parameter Sniffing Problem


Problem Statement: The problem with Filtered index is, when a local variable is passed as a parameter to the query, Query Optimizer does not select the correct Index (Filtered index) even though the WHERE  condition (the selectivity) falls in the range of Filtered index.

Again I am using AdventureWork2008R2 database for explanation. We will make use of Person.Person table.

We are interested to fire this query
select PersonType  from Person.Person where PersonType = 'SP'

Here I am creating 2 indexes on the PersonType column, one Non Custered Index and another Filtered Index

create index ix_Person_PersonType
on Person.Person(PersonType)
Command(s) completed successfully.

create index fix_Person_PersonType
on Person.Person(PersonType) where PersonType = 'SP'
Command(s) completed successfully.

Now our indexes are in place, lets fire query to see the execution plan.

We are firing the query against Person table for PersonType like ‘SP’, and expecting Optimizer to pick up newly created Filtered Index.

select PersonType  from Person.Person where PersonType = 'SP'



Thats fantastic it picked up the right index what we were expecting. Here in the above query we have explicitly passed the value in the where predicate that is PersonType = ‘SP’

Let see what Optimizer will do if we pass the value through local variable.

declare @para varchar(2) = 'SP'
select PersonType  from Person.Person where PersonType = @para



Optimizer did not use the Filtered index even though the where predicate search the same records.

The reason behind is Optimizer don’t have idea what value the local variable will hold at compile time, so it trade off Filtered Index and choose the Non clustered index.

Workaround Solution: Option(RECOMPILE) we can use the statement level recompile statement to tell the engine to recompile the plan at execution time.

declare @para varchar(2) = 'SP'
select PersonType  from Person.Person where PersonType = @para

Here in this query we can see the @para value is not available at compile time only at run time it is exposed. So the alternative solution would be recompiling the  plan at run time with @para value is available.

declare @para varchar(2) = 'SP'
select PersonType  from Person.Person  where PersonType = @para option(recompile)



So the execution pan clearly tells that when you use execution level RECOMPILE option the correct index will be picked up. Still try to avoid statement level recompilation and make use of stored cache plan. This is just an work around solution.

If your code has bliss then do you think you need to go out in search of materialized happiness?? J


Post Reference: Vikram Aristocratic Elfin Share 

Monday, July 28, 2014

Parameter Sniffing with Filtered Index


Filtered Index is one of the fantastic advancement in the indexing arena, but with each great feature there is always a trade off, which we called it as an exception. The problem with Filtered index is, when a local variable is passed as a parameter to the query, Query Optimizer does not select the correct Index (Filtered index) even though the WHERE  condition (the selectivity) falls in the range of Filtered index.

Let’s take an example to clarify our point. Here I am using AdventureWork2008R2 database for explanation. We will make use of Person.Person table.

We are interested to fire this query
select PersonType  from Person.Person where PersonType = 'SP'

Now let me check Is there any index build up for PersonType column.

select name from sys.indexes where object_id = OBJECT_ID('Person.Person')
AK_Person_rowguid
PXML_Person_AddContact
PXML_Person_Demographics
XMLPATH_Person_Demographics
XMLPROPERTY_Person_Demographics
XMLVALUE_Person_Demographics

Ah! There is no index created on PersonType column, why wait lets create it

Here I am creating 2 indexes on the PersonType column, one Non Custered Index and another Filtered Index

create index ix_Person_PersonType
on Person.Person(PersonType)
Command(s) completed successfully.

create index fix_Person_PersonType
on Person.Person(PersonType) where PersonType = 'SP'
Command(s) completed successfully.

Lets fire the query to see our indexes created

select name from sys.indexes where object_id = OBJECT_ID('Person.Person')
AK_Person_rowguid
ix_Person_PersonType
fix_Person_PersonType
PXML_Person_AddContact
PXML_Person_Demographics
XMLPATH_Person_Demographics
XMLPROPERTY_Person_Demographics
XMLVALUE_Person_Demographics

Now our indexes are in place, its time to fire query to see whether optimizer is picking up correct index.

We are firing the query against Person table for PersonType like ‘SP’, and expecting Optimizer to pick up newly created Filtered Index.

select PersonType  from Person.Person where PersonType = 'SP'


Thats fantastic it picked up the right index what we were expecting. Here in the above query we have explicitly passed the value in the where predicate that is PersonType = ‘SP’

Let see what Optimizer will do if we pass the value through local variable.

declare @para varchar(2) = 'SP'
select PersonType  from Person.Person where PersonType = @para



Optimizer did not use the Filtered index even though the where predicate search the same records.

The reason behind is Optimizer don’t have idea what value the local variable will hold at compile time, so it trade off Filtered Index and choose the Non clustered index.

Conclusion: If your query hold local variable as a parameter passed, then try to avoid creating Filtered index on the same.

If something very precious to you is parting u, no need to pain up, there must be some good reason, don’t be sad, best moment yet to come, still waiting for u at your door step, knocking; listen the beauty of her knock, Celebrate happiness my friend. Here it goes happiness of completing 50th article of the year, thank you my friend   :)


Post Reference: Vikram Aristocratic Elfin Share

Monday, January 6, 2014

Plan Caching VI - Forced Parameterization in Adhoc Queries

Read Previous Post:
In the last article we saw that the datatype of constant is selected on fly depending upon the constant value by SQL Server, due to which the cached Prepared Parameterized plan is not been used by a similar query with constant little different.

Just check this, below example.  

dbcc FREEPROCCACHE
DBCC DROPCLEANBUFFERS
Go

select * from emp where emp_id = 1
go

Then executing this sys.dm_exec_cached_plans query

select usecounts, cacheobjtype, objtype, [text]
from sys.dm_exec_cached_plans
CROSS APPLY sys.dm_exec_sql_text(plan_handle)
where usecounts > 0
AND
[text] not like '%dm_exec_cached_plans%' and [text] not like  '%dm_exec_sql_text%'
order by usecounts desc
go

usecounts cacheobjtype  objtype  text
--------  ------------- -------- --------------------------------------------
1         Compiled Plan Prepared (@1 tinyint)SELECT * FROM emp WHERE emp_id=@1
1         Compiled Plan Adhoc    select * from emp where emp_id = 1

From the output we can see that the constant used in query where predict is being treated as tinyint. So the datatype is of the constant is being selected on fly by SQL Server while making a prepared Parameterized cache plan.

Now let’s query the same select with contact value ranging out of tiny data type.

select * from emp where emp_id = 700
go
 
Then executing this sys.dm_exec_cached_plans query

select usecounts, cacheobjtype, objtype, [text]
from sys.dm_exec_cached_plans
CROSS APPLY sys.dm_exec_sql_text(plan_handle)
where usecounts > 0
AND
[text] not like '%dm_exec_cached_plans%' and [text] not like  '%dm_exec_sql_text%'
order by usecounts desc
go

usecounts cacheobjtype  objtype  text
--------  ------------- -------- --------------------------------------------
1         Compiled Plan Prepared (@1 tinyint)SELECT * FROM emp WHERE emp_id=@1
1         Compiled Plan Adhoc    select * from emp where emp_id = 1
1         Compiled Plan Prepared (@1 smallint)SELECT * FROM emp WHERE emp_id=@1
1         Compiled Plan Adhoc    select * from emp where emp_id = 700

Here we can see from the output that it has not used the Prepared plan which gets generated when we executed query with predicate constant value as 1 and generate a new prepared plan and cached in memory; this time the constant is replaced by parameter and the type for the parameter is choosed as smallInt unlike tinyint in previous case.

So we get two prepared queries plan with two different parameter data type. So SQL Server is not making use of existing Parameterized cached plan.

The only way to force SQL Server to use the same data type  for both the queries is to enable PARAMETERIZED FORCED for the database.

dbcc FREEPROCCACHE
DBCC DROPCLEANBUFFERS
Go

alter database sparsh set parameterization forced;
go

Here we have set PARAMETERIZATION FORCED enabled. Once this is done SQL Server treats every kind constants as just a set of parameters.

Now since we have enabled PARAMETERIZATION FORCED. Lets run both the select query and query the dm_exec_cached_plans to check the output.

select * from emp where emp_id = 1
go
select * from emp where emp_id = 700
go

select usecounts, cacheobjtype, objtype, [text]
from sys.dm_exec_cached_plans
CROSS APPLY sys.dm_exec_sql_text(plan_handle)
where usecounts > 0
AND
[text] not like '%dm_exec_cached_plans%' and [text] not like  '%dm_exec_sql_text%'
order by usecounts desc
go   

usecounts cacheobjtype  objtype  text
--------  ------------- -------- --------------------------------------------
2         Compiled Plan Prepared (@0 int)SELECT * FROM emp WHERE emp_id=@0
1         Compiled Plan Adhoc    select * from emp where emp_id = 1
1         Compiled Plan Adhoc    select * from emp where emp_id = 700

Here we can see that no new Prepared plan gets generated, instead the same prepared plan is referred by the second query with emp_id = 700. And the data type chosen by SQL Server for constant is INT which satisfy both the constant.

Though you need to be careful while setting this
parameterization forced” since setting this option “on” for the database, you are letting SQL Server assume and treat all constant as a same parameter, which may give poor performance. Alternatives are available, which I will soon post as an article.

If my girl friend judge my love to code is like putting them in a strait jacket and kicking them down a flight, then yes.. I have deep love to my code K   

Post Reference: Vikram Aristocratic Elfin Share