Forcer SQL Server à exécuter les conditions de requête telles qu'elles sont écrites?

14

J'utilise SQL Server 2008 R2 et j'ai cette pseudo requête (SP):

select ...
from ...
WHERE    @LinkMode IS NULL
     AND (myColumn IN (...very long-running query...))
     ...
     ...

Le problème est que l'exécution de la requête prend très longtemps - même si j'exécute le SP avec @LinkMode=2.

Comme vous l'avez remarqué, la requête de longue durée ne doit être exécutée que si @LinkMode est null, ce qui n'est pas le cas ici. Dans mon cas, @LinkMode = 2!

Cependant, si je le change en:

 select ...
    from ...
    WHERE    1=2
         AND (myColumn IN (...very long time exeted query...))
     ...
     ...

le SP ne fonctionne rapidement.

J'ai entendu auparavant que parfois l'optimiseur peut optimiser l'ordre des critères.

Alors je demande :

  • Même si l'optimiseur choisit un itinéraire différent, quoi de plus rapide que de vérifier si =null? Je veux dire, je pense que la vérification if a==nullest beaucoup plus rapide que l'exécution de l'autre longue requête ...

  • Comment puis-je forcer SQL Server à exécuter la requête telle que je l'ai écrite (dans le même ordre)?

Royi Namir
la source

Réponses:

22

Vous tombez dans le piège " Catch-All Query ", qui est très bien expliqué par Gail Shaw ici .

Pour résumer le problème: SQL Server optimise la surcharge importante de la compilation de requêtes en mettant en cache un plan de requête après la compilation, puis en vérifiant ultérieurement le cache pour un plan de requête correspondant avant une compilation ultérieure. La "correspondance" qui se produit ici est purement textuelle, donc la valeur réelle d'une variable n'affectera pas cela.

C'est bien 99% du temps, mais dans certains cas c'est mauvais . Un cas où c'est mauvais est quand quelqu'un essaie de construire une clause WHERE comme si c'était comme une instruction IF en court-circuit en C, etc. Cela ne fonctionne pas bien, car le compilateur SQL doit faire un plan de requête qui fonctionnera indépendamment de ce que sont réellement les valeurs des paramètres, et la seule façon dont il peut gérer ces conditions de commutation logiques "intelligentes" dans la clause WHERE est de faire un plan de force brute simple qui balaye tout le tableau, filtrant les lignes au fur et à mesure , sans exploiter aucun index.

Sans surprise, cela les rend uniformément lents, quels que soient les paramètres / valeurs des variables.

RBarryYoung
la source
8

Il n'existe aucun moyen garanti de forcer SQL Server à exécuter vos conditions de clause dans une séquence spécifique. L'optimiseur les évaluera toujours dans l'ordre qu'il juge approprié.

Ce que vous pouvez faire est quelque chose comme ceci:

IF @LinkMode IS NULL
BEGIN
    select ...
    from ...
    WHERE (myColumn IN (...very long time exeted query...))
         ...
         ...
END
ELSE
BEGIN
    select ...
    from ...
    WHERE ...
         ...
END
Nom d'écran ésotérique
la source
3

S'il s'agit d'une option, utilisez une instruction IF pour exécuter la forme appropriée de la requête. De plus, en SQL, vous dites au moteur de base de données quoi faire, pas comment le faire - les choses ne sont pas exécutées du début à la fin. Il peut être difficile de prédire exactement ce qu'il fera. Mais vous le savez probablement;)

Sam
la source
2

Le SQL dynamique fonctionnerait probablement aussi, car dans ce cas, l'optimiseur de requête devrait obtenir les valeurs réelles au moment de l'exécution (corrigez-moi si je me trompe, je ne suis pas sûr, mais semble me rappeler de l'utiliser dans des situations similaires) . Mais je suis avec les autres dans celui-ci, dans la mesure où une clause IF / ELSE vous servirait le mieux, car c'est la solution la plus simple et la plus facile qui fera exactement ce qui est nécessaire.

Pour référence future au cas où vous ne l'auriez pas encore utilisé, un site horriblement laid avec un exemple de travail pour SQL dynamique peut être trouvé ici par exemple: http://sqlusa.com/bestpractices/dynamicsql/

Kahn
la source
1

Je recommanderais la construction IF / ELSE .. Si pour une raison qui ne fonctionne pas pour vous, vous pouvez toujours envisager d'utiliser l'option WITH RECOMPILE ..

timvw
la source
Pourriez-vous peut-être nous expliquer à quoi pourrait ressembler la «construction if / else»? : D
jcolebrand
J'allais suggérer d'utiliser OPTION (AVEC RECOMPILE), car cela générerait un plan idéal à chaque fois - le délai de compilation ajouterait des frais généraux, mais je pense que c'est mieux dans l'ensemble dans ce cas.
SqlRyan