Quantcast
Channel: SQL Server Database Engine forum
Viewing all articles
Browse latest Browse all 15930

Parameters at the first call of a stored proc might produce wrong execution plans

$
0
0

Hi everyone,

Sometimes, the parameters used the first time a stored procedure is run may produce an specific execution plan that is cached and reused for the next times the procedure is run, with other parameters, and then might cause long running processes that could be faster.

For example, consider the following code:

CREATE TABLE TableA (Groupe int, Identifiant bigint) 
CREATE TABLE TableB (Groupe int, Identifiant bigint) 
CREATE UNIQUE CLUSTERED INDEX IA ON TableA(Groupe,Identifiant) 
CREATE UNIQUE CLUSTERED INDEX IB ON TableB(Groupe,Identifiant) 
;WITH Liste(Id) AS 
(SELECT 1 
UNION ALL 
SELECT Id+1 FROM Liste WHERE Id<1000000) 
INSERT INTO TableA 
SELECT g,Id 
from Liste 
cross join (values(1),(2)) groupes(g) 
option (maxrecursion 0) 
insert into TableB (Groupe,Identifiant) 
select Groupe,Identifiant 
from TableA 
where Groupe=1 
CREATE TYPE TypeListe as table (Id bigint) 
GO 
CREATE PROCEDURE Traitement 
@Liste dbo.TypeListe READONLY, 
@Groupe int 
AS 
BEGIN 
CREATE TABLE #Liste (Id bigint) 
CREATE TABLE #Id (Id bigint) 
INSERT INTO #Id(Id) 
select Id+Offset 
from @Liste 
cross join (values (-5),(-4),(-3),(-2),(-1),(0),(1),(2),(3),(4),(5)) Delta(Offset) 
insert into #Liste(Id) 
select t.Identifiant 
from #Id l 
join TableA t on t.Identifiant =l.Id 
WHERE t.Groupe=@Groupe 
select count(*) as Nb 
from #Liste l 
join TableB t on t.Identifiant=l.Id 
WHERE t.Groupe=@Groupe 
END 
GO 

If the stored procedure is first called on one group of data, the cached plan might be very bad for the other group of data.

DBCC FREEPROCCACHE 
declare @l dbo.TypeListe 
;with paquet(centaine) as 
(select 1 
union all 
select centaine+1 from paquet where centaine<50) 
insert into @l(Id) 
select 100*centaine from paquet 
exec Traitement @liste=@l,@Groupe=2 
GO 
--> duration : a couple of ms 
declare @l dbo.TypeListe 
insert into @l(Id) values (1000) 
exec traitement @Liste=@l,@Groupe=1 
--> duration : half a second !!! 

And if the first call of the stored procedure uses other parameters, it could be good for both usages.

DBCC FREEPROCCACHE 
declare @l dbo.TypeListe 
insert into @l(Id) values (1000) 
exec traitement @Liste=@l,@Groupe=1 
GO 
--> duration : a couple of ms 
declare @l dbo.TypeListe 
;with paquet(centaine) as 
(select 1 
union all 
select centaine+1 from paquet where centaine<50) 
insert into @l(Id) 
select 100*centaine from paquet 
exec Traitement @liste=@l,@Groupe=2 
--> duration : a couple of ms 

Did you already encounter this kind of problem? How did you solve it?
- OPTIMIZE FOR : not necessarily a good idea in every case, because @Groupe values might not be known at design time
- WITH RECOMPILE : could be a problem if the stored procedure is called very often
- Table or statement hints (FORCE ORDER, ...) : why not ? Not considered as a best practice, but it works well...

Your opinion?

JN.


Jean-Nicolas BERGER
http://blog.sqlserver.fr


Viewing all articles
Browse latest Browse all 15930

Trending Articles