We've been looking through some slow executing SQL today and came across what at first seemed like a strange occurrence whereby if we ran the contents of the stored proc in Management Studio it executed in a second, but when the stored proc was run it took over 4 minutes.
We looked at the execution plans for each and they appeared to be different, after doing some searching on the internet we came across the term for our particular issue "parameter sniffing" -
"Parameter sniffing" refers to a process whereby SQL Server's execution environment "sniffs" the current parameter values during compilation or recompilation, and passes it along to the query optimizer so that they can be used to generate potentially faster query execution plans. The word "current" refers to the parameter values present in the statement call that caused a compilation or a recompilation.
It seems that when a stored procedure is first executed SQL (cleverly) stores an execution plan based on the parameters and then all subsequent calls to the stored proc use exactly the same execution plan regardless of the supplied parameters (in our case this was true).
The problems then occur when different parameters are supplied which require different execution plans. When running dynamic SQL, SQL Server makes up the plan on the fly this can be time consuming but it uses the latest stats it has available, whereas the stored proc uses the original plan which is faster but can be potentially wrong.
In order to speed the stored procedure execution we tried adding "with recompile" before the AS command in the stored procedure however this seemed to make no difference and the system continued to use the original execution plan.
Our second approach was to change the way the parameters were used in the stored proc, for example a simplified version of our original stored proc was:
create procedure MyProc(@date datetime)
as begin
select * from myTable where date<@date
end
We then changed the proc to
create procedure MyProc(@date datetime)
as begin
declare @localDate datetime
Set @localdate = @date
select * from myTable where date<@localDate
end
Once we used a local parameter rather than the supplied parameter in the query, we found the stored proc ran at the same speed as the dynamic SQL.
So in conclusion if you use a lot of varying values in your stored proc, or over time the execution speed slows right down then I suggest reading up on Parameter Sniffing and maybe implementing our approach of using local variables.