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