web-dev-qa-db-fra.com

Le court-circuit de la clause SQL WHERE est-il évalué?

Les expressions booléennes dans les clauses SQL WHERE évaluées en court-circuit ?

Par exemple:

SELECT * 
FROM Table t 
WHERE @key IS NULL OR (@key IS NOT NULL AND @key = t.Key) 

Si @ key IS NULL est évalué à true, est @ key IS NOT NULL ET @key = t.Key évalué?

Si non, pourquoi pas?

Si oui, est-ce garanti? Fait-il partie de ANSI SQL ou est-il spécifique à la base de données?

Si spécifique à la base de données, SqlServer? Oracle? MySQL?

133
Greg Dean

ANSI SQL Draft 2003 5WD-01-Framework-2003-09.pdf

6.3.3.3 Ordonnance d'évaluation des règles

[...]

Lorsque la priorité n'est pas déterminée par les formats ou par les parenthèses, l'évaluation efficace des expressions est généralement effectuée de gauche à droite. Cependant, c'est dépendant de l'implémentation si les expressions sont réellement évaluées de gauche à droite, en particulier lorsque des opérandes ou des opérateurs peuvent provoquer des conditions à lever ou si les résultats des expressions peuvent être déterminés sans complètement évaluer toutes les parties de l'expression.

67
Mert Sonsuz

De ce qui précède, le court-circuit n'est pas vraiment disponible.

Si vous en avez besoin, je suggère une déclaration de cas:

Where Case when Expr1 then Expr2 else Expr3 end = desiredResult

Expr1est toujours évalué, mais un seul parmi Expr2 et Expr3 sera évalué par ligne.

57
PMc

Je pense que c'est l'un des cas où je l'écrirais comme s'il n'avait pas été court-circuité, pour trois raisons.

  1. Parce que pour MSSQL, ce n'est pas résolu en regardant BOL à l'endroit évident, donc pour moi, cela le rend canoniquement ambigu.

  2. parce qu'au moins je sais que mon code fonctionnera. Et plus important encore, il en sera de même pour ceux qui viendront après moi, donc je ne les installe pas pour s'inquiéter encore et encore de la même question.

  3. J'écris assez souvent pour plusieurs produits SGBD, et je ne veux pas me souvenir des différences si je peux les contourner facilement.

18
dkretz

Je ne crois pas que les courts-circuits dans SQL Server (2005) soient garantis. SQL Server exécute votre requête via son algorithme d'optimisation qui prend en compte de nombreux éléments (index, statistiques, taille de table, ressources, etc.) pour proposer un plan d'exécution efficace. Après cette évaluation, vous ne pouvez pas dire avec certitude que votre logique de court-circuit est garantie.

J'ai moi-même rencontré la même question il y a quelque temps et mes recherches ne m'ont vraiment pas donné de réponse définitive. Vous pouvez écrire une petite requête pour vous donner une idée de son fonctionnement, mais pouvez-vous être sûr qu'à mesure que la charge sur votre base de données augmente, les tables deviennent plus grandes et les choses sont optimisées et modifiées dans la base de données, cette conclusion sera tenir. Je ne pouvais pas et j'ai donc commis une erreur par prudence et j'ai utilisé la clause CASE in WHERE pour assurer un court-circuit.

12
Mehmet Aras

Vous devez garder à l'esprit le fonctionnement des bases de données. Étant donné une requête paramétrée, la base de données crée un plan d'exécution basé sur cette requête sans les valeurs des paramètres. Cette requête est utilisée à chaque exécution de la requête, quelles que soient les valeurs réelles fournies. Si les courts-circuits de requête avec certaines valeurs n'auront pas d'importance pour le plan d'exécution.

7
Logicalmind

J'utilise généralement cela pour les paramètres facultatifs. Est-ce la même chose que le court-circuit?

SELECT  [blah]
FROM    Emp
WHERE  ((@EmpID = -1) OR (@EmpID = EmpID))

Cela me donne la possibilité de passer -1 ou autre chose pour tenir compte de la vérification facultative d'un attribut. Parfois, cela implique de se joindre à plusieurs tables, ou de préférence à une vue.

Très pratique, pas tout à fait sûr du travail supplémentaire qu'il donne au moteur db.

3
p.campbell

Pour SQL Server, je pense que cela dépend de la version mais mon expérience avec SQL Server 2000 est qu'il évalue toujours @key = t.Key même lorsque @key est nul. En d'autres termes, il n'effectue pas de court-circuit efficace lors de l'évaluation de la clause WHERE.

J'ai vu des gens recommander une structure comme votre exemple comme moyen de faire une requête flexible où l'utilisateur peut entrer ou non différents critères. Mon observation est que Key est toujours impliqué dans le plan de requête lorsque @key est nul et si Key est indexé, il n'utilise pas l'index de manière efficace.

Ce type de requête flexible avec des critères variables est probablement un cas où le SQL créé dynamiquement est vraiment la meilleure façon de procéder. Si @key est nul, vous ne l'incluez simplement pas du tout dans la requête.

2
tetranz

je ne connais pas la courte circulation, mais je l'écrirais comme une déclaration if-else

if (@key is null)
begin

     SELECT * 
     FROM Table t 

end
else
begin

     SELECT * 
     FROM Table t 
     WHERE t.Key=@key

end

en outre, les variables doivent toujours se trouver du côté droit de l'équation. cela le rend sargable.

http://en.wikipedia.org/wiki/Sargable

2
DForck42

Je suis juste tombé sur cette question et j'avais déjà trouvé cette entrée de blog: http://rusanu.com/2009/09/13/on-sql-server-boolean-operator-short-circuit/

Le serveur SQL est libre d'optimiser une requête où bon lui semble, donc dans l'exemple donné dans le billet de blog, vous ne pouvez pas vous fier au court-circuit.

Cependant, un CAS est apparemment documenté pour être évalué dans la commande écrite - vérifiez les commentaires de ce billet de blog.

2
stolsvik

Ci-dessous un test rapide et sale sur SQL Server 2008 R2:

SELECT *
FROM table
WHERE 1=0
AND (function call to complex operation)

Cela revient immédiatement sans enregistrements. Une sorte de comportement de court-circuit était présente.

Ensuite, j'ai essayé ceci:

SELECT *
FROM table
WHERE (a field from table) < 0
AND (function call to complex operation)

le fait de ne connaître aucun dossier satisferait à cette condition:

(a field from table) < 0

Cela a pris plusieurs secondes, indiquant que le comportement de court-circuit n'était plus là et que l'opération complexe était évaluée pour chaque enregistrement.

J'espère que cela aide les gars.

1
Jorge

La caractéristique principale de l'évaluation de court-circuit est qu'elle arrête d'évaluer l'expression dès que le résultat peut être déterminé. Cela signifie que le reste de l'expression peut être ignoré car le résultat sera le même, qu'il soit évalué ou non.

Les opérateurs booléens binaires sont comutatifs, ce qui signifie:

a AND b == b AND a
a OR  b == b OR  a
a XOR b == b XOR a

il n'y a donc aucune garantie sur l'ordre d'évaluation. L'ordre d'évaluation sera déterminé par l'optimiseur de requêtes.

Dans les langues avec des objets, il peut y avoir des situations où vous pouvez écrire des expressions booléennes qui ne peuvent être évaluées qu'avec une évaluation de court-circuit. Votre exemple de construction de code est souvent utilisé dans de tels langages (C #, Delphi, VB). Par exemple:

if(someString == null | someString.Length == 0 )
  printf("no text in someString");

Cet exemple C # provoquera une exception si someString == null car il sera entièrement évalué. En évaluation de court-circuit, cela fonctionnera à chaque fois.

SQL ne fonctionne que sur des variables scalaires (pas d'objets) qui ne peuvent pas être non initialisées, il n'y a donc aucun moyen d'écrire une expression booléenne qui ne peut pas être évaluée. Si vous avez une valeur NULL, toute comparaison retournera false.

Cela signifie qu'en SQL, vous ne pouvez pas écrire une expression évaluée différemment selon l'utilisation d'un court-circuit ou d'une évaluation complète.

Si l'implémentation SQL utilise une évaluation de court-circuit, elle ne peut qu'espérer accélérer l'exécution des requêtes.

1
zendar

Voici une démo pour prouver que MySQL effectue un court-circuitage de la clause WHERE :

http://rextester.com/GVE488

Cela exécute les requêtes suivantes:

SELECT myint FROM mytable WHERE myint >= 3 OR myslowfunction('query #1', myint) = 1;
SELECT myint FROM mytable WHERE myslowfunction('query #2', myint) = 1 OR myint >= 3;

La seule différence entre ceux-ci est l'ordre des opérandes dans la condition OR.

myslowfunction dort délibérément pendant une seconde et a pour effet secondaire d'ajouter une entrée à une table de journal à chaque exécution. Voici les résultats de ce qui est enregistré lors de l'exécution des deux requêtes ci-dessus:

myslowfunction called for query #1 with value 1
myslowfunction called for query #1 with value 2
myslowfunction called for query #2 with value 1
myslowfunction called for query #2 with value 2
myslowfunction called for query #2 with value 3
myslowfunction called for query #2 with value 4

Ce qui précède montre qu'une fonction lente est exécutée plusieurs fois lorsqu'elle apparaît sur le côté gauche d'une condition OR lorsque l'autre opérande n'est pas toujours vrai (en raison d'un court-circuit)).

1
Steve Chambers

Cela prend 4 secondes supplémentaires dans l'analyseur de requêtes, donc d'après ce que je peux voir, IF n'est même pas court-circuité ...

SET @ADate = NULL

IF (@ADate IS NOT NULL)
BEGIN
    INSERT INTO #ABla VALUES (1)
        (SELECT bla from a huge view)
END

Ce serait bien d'avoir un chemin garanti!

0
Will