web-dev-qa-db-fra.com

Meilleur plan d'exécution si le paramètre est stocké dans une variable locale

J'ai deux procédures stockées. Celui-ci est incroyablement rapide (~ 2 secondes)

CREATE PROCEDURE [schema].[Test_fast]
   @week date
AS
BEGIN

    declare @myweek date = @week

    select distinct serial 
    from [schema].[tEventlog]         as e
    join [schema].tEventlogSourceName as s on s.ID = e.FKSourceName
    where s.SourceName = 'source_name'
        and (e.EventCode = 1 or e.EventCode = 9)
        and cast(@myweek as datetime2(3)) <= [Date] 
        and [Date] < dateadd(day, 7, cast(@myweek as datetime2(3)))    
END

Et celui-ci fonctionne lentement (environ 2 heures):

create PROCEDURE [schema].[Test_slow]
   @week date
AS
BEGIN

    select distinct serial 
    from [schema].[tEventlog]         as e
    join [schema].tEventlogSourceName as s on s.ID = e.FKSourceName
    where s.SourceName = 'source_name'
        and (e.EventCode = 1 or e.EventCode = 9)
        and cast(@week as datetime2(3)) <= [Date] 
        and [Date] < dateadd(day, 7, cast(@week as datetime2(3)))
END

La seule différence réelle est la ligne (en utilisant la variable locale @myweek):

declare @myweek date = @week

Voici les plans d'exécution. Le premier plan provient de [Schema]. [Test_fast] et la seconde provient de [Schema]. [Test_slow]:

enter image description here

Ma question est la suivante: pourquoi SQL Server 2012 a-t-il un plan d'exécution bien meilleur (plus rapide) lorsque je prends le paramètre et le stocke dans une variable locale, puis utilisez cette variable locale. Y a-t-il quelque chose de brisé avec les statistiques ou les index? (Je me demande également pourquoi le deuxième plan d'exécution n'utilise aucun type d'exécution parallèle).

[~ # ~] Mise à jour [~ # ~] :

Je donne le même paramètre de 2 SPS et les a commencé dans le même temps (presque 2s diffèro) La mise à jour automatique de la statistique dans cette DB.

exemple:

EXEC    [schema].[Test_fast]
        @week = '2016-02-08'

EXEC    [schema].[Test_slow]
        @week = '2016-02-08'

Voici le plan d'exécution:

https://gist.github.com/anonymous/6e404f896d9613c2061a#file-sp_execution_plan-sqlplan

Une mise à jour supplémentaire de l'indice n'a également aucun effet.

8
Leon

L'utilisation de variables locales empêche la renonciation des valeurs de paramètre, les requêtes sont donc compilées sur la base de Statistiques de distribution moyenne . C'était la solution de contournement pour certains types de problèmes de sensibilité des paramètres avant OPTION (OPTIMIZE FOR UNKNOWN) et Drapeau de trace 4136 est devenu disponible.

Du plan d'exécution fourni, c'est exactement ce qui s'est passé dans votre cas.

Lorsqu'une variable locale est utilisée, la valeur de la variable ne peut pas être reniflé:

No sniffing

Notez la "valeur compilée" vide. L'optimiseur de requête estime un plus grand nombre de lignes en fonction de la répartition moyenne des valeurs de la colonne Date , (ou éventuellement une supposition complète) menant au parallèle planifier.

Lorsque le paramètre de procédure stockée est utilisé directement, la valeur de @Week est reniflé:

Sniffed

L'optimiseur estime que le nombre de lignes correspondant aux prédicats de la requête en utilisant la valeur '2016-02-08', branchée sur:

and cast(@week as datetime2(3)) <= [Date] 
and [Date] < dateadd(day, 7, cast(@week as datetime2(3)))

Il sort avec une estimation d'une ligne, conduisant au choix d'un plan de série avec la recherche clé. Les prédicats ci-dessus ne sont pas très amicaux pour l'estimation de la cardinalité. L'estimation de 1 ligne peut donc ne pas être très précise. Vous pouvez essayer d'activer le drapeau de trace 4199 mais rien ne garantit que l'estimation s'améliorera.

Pour plus de détails, veuillez vous reporter:

Paramètre reniflant, incorporation et les options RECOMPILE

En général, il est également possible que la course initiale de la procédure stockée se produise avec une valeur très sélective pour @Week, avec seulement un petit nombre de rangées attendues. Une autre cause possible des problèmes se produit lorsqu'une valeur très récente de @Week est utilisée à l'appel initial, avant que les statistiques soient mises à jour pour couvrir cette plage de valeurs (c'est le problème de clé ascendant ).

Une valeur renifiée très sélective pour @Week peut provoquer une optimisation de la requête de choisir un plan non parallèle avec une recherche d'index et une recherche clé. Ce plan sera mis en cache pour la réutilisation des exécutions futures de la procédure avec différentes valeurs de paramètres. Lorsqu'une exécution ultérieure (avec une valeur différente pour @Week) sélectionne beaucoup plus de lignes que d'origine, le plan est susceptible de fonctionner mal, car la recherche de la clé + la recherche n'est plus une bonne stratégie.

16
Paul White 9