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

142

Les expressions booléennes dans les clauses SQL WHERE sont -elles é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 prend la valeur true, est-ce que @key IS NOT NULL AND @key = t.Key est évalué?

Si non, pourquoi pas?

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

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

Greg Dean
la source
La clause @key IS NOT NULL n'est-elle pas redondante? La clause @key IS NULL sur le LHS prend en charge ce non?
dépenser le
10
@splender - dépend de la réponse à la question
Greg Dean
@Greg: Je suis d'accord avec Spender. Je ne vois pas l'absence ou la présence de court-circuit faire une différence. Si @key EST NULL, alors @key = t.Key retournera toujours false, comme NULL! = NULL (c'est pourquoi nous utilisons IS NULL, après tout).
Michael Madsen
14
@Michael et @spender - Le but de la question est de savoir si la deuxième condition est évaluée ou non. Le point de la question n'est pas, est cette instruction SQL spécifique écrite en aussi peu de caractères que possible. Dans des exemples plus compliqués, cela importerait sans aucun doute, comme si la clause where court-circuite, vous pourriez écrire des expressions qui seraient autrement erronées.
Greg Dean
2
Le court-circuit implique l'évaluation des conditions de gauche à droite. Étant donné une condition telle WHERE a = 1 AND b = 2qu'il pourrait être efficace pour le moteur de base de données de trouver toutes les lignes où b = 2 en premier, puis filtrer où a = 1. Si vous demandez une garantie, l'optimiseur devient inutile.
Salman A

Réponses:

72

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

6.3.3.3 Ordre d'évaluation des règles

[...]

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

Communauté
la source
4
Dépend de la mise en œuvre? Génial. Bon à savoir aussi. Au moins CASEest court-circuité.
dakab
3
Cela ne signifie-t-il pas que les évaluations des expressions sont mal définies? "(0 = 0 OR NULL)", est toujours NULL si tous les termes sont évalués, mais toujours vrai s'il est évalué de gauche à droite et court-circuité.
user48956
7
SQL est un langage déclaratif, il exprime fondamentalement la logique du calcul sans décrire son flux de contrôle; ce qui contredit en quelque sorte le style impératif de l'évaluation des courts-circuits et ses conséquences.
Jorge Garcia
Je n'y avais pas pensé de cette façon @JorgeGarcia. Je suppose que l'évaluation des courts-circuits force implicitement un ordre sur les opérations. Je suis aux prises avec un code où cela est peut-être à l'origine d'un problème subtil. Merci pour la perspicacité.
Carnot Antonio Romero
58

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

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

Where Case when Expr1 then Expr2 else Expr3 end = desiredResult

Expr1est toujours évalué, mais un seul parmi Expr2et Expr3sera évalué par ligne.

PMc
la source
3
Cela dépend de l'implémentation du SGBDR, je suppose. Pour SQL Server au moins, il existe au moins une exception documentée pour ne pas montrer ce comportement (c.-à-d. Court-circuit); cf CASE (Transact-SQL) - Remarques . J'ai cité ce cas dans cette réponse que j'ai donnée sur la question Sql - Ordre explicite des conditions WHERE? .
TT.
1
Expression de cas , pas déclaration.
jarlh
19

Je pense que c'est l'un des cas où je l'écrirais comme s'il ne court-circuitait pas, 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, ceux qui viendront après moi le seront aussi, donc je ne les incite pas à s'inquiéter à travers la même question encore et encore.

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

dkretz
la source
4
Excellente suggestion. Cela ne répond pas à la question, mais c'est un grand point de vue pragmatique. so +1
Greg Dean
12

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 pas vraiment donné de réponse définitive. Vous pouvez écrire une petite requête pour vous donner un sentiment de preuve que cela fonctionne, mais pouvez-vous être sûr qu'à mesure que la charge de 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 excès de prudence et utilisé la clause CASE in WHERE pour assurer un court-circuit.

Mehmet Aras
la source
7

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 construit un plan d'exécution basé sur cette requête sans les valeurs des paramètres. Cette requête est utilisée chaque fois que la requête est exécutée, quelles que soient les valeurs réellement fournies. Que la requête court-circuite avec certaines valeurs n'aura pas d'importance pour le plan d'exécution.

Esprit logique
la source
6
c'est important pour la vitesse d'exécution!
user4951
Ce n'est pas parce que c'est ainsi que cela fonctionne actuellement qu'il ne peut pas être changé. Nous devons séparer le modèle / la sémantique de la mise en œuvre. Les plans d'exécution sont mis en œuvre en interne pour optimiser l'exécution des requêtes ... et la sémantique des courts-circuits non seulement contredit la nature déclarative de SQL mais peut contraindre de telles optimisations. Cependant, si la sémantique d'évaluation des courts-circuits devait être prise en charge par le SGBD, la mise en œuvre des plans d'exécution changerait pour prendre en charge une telle sémantique.
Jorge Garcia
3

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

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

Cela me donne la possibilité de passer -1 ou autre pour tenir compte de la vérification facultative d'un attribut. Parfois, cela implique de rejoindre 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.

p.campbell
la source
2

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 efficacement.

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

tétranz
la source
2

Je viens de trébucher 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 partout où il le juge opportun, donc dans l'exemple donné dans l'article de blog, vous ne pouvez pas compter sur un court-circuit.

Cependant, un CASE est apparemment documenté à évaluer dans l'ordre écrit - vérifiez les commentaires de ce billet de blog.

Stolsvik
la source
1

La principale caractéristique de l'évaluation des courts-circuits 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 langages avec objets, il peut y avoir des situations dans lesquelles 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 ces langages (C #, Delphi, VB). Par exemple:

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

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

SQL fonctionne uniquement 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 renverra false.

Cela signifie qu'en SQL, vous ne pouvez pas écrire une expression évaluée différemment en fonction de 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.

zendar
la source
1
Oui, les opérateurs booléens sont commutatifs. Je ne pense pas que les objets (ou pas) aient quoi que ce soit à voir avec cela.
Greg Dean
1

Je ne sais pas pour une courte diffusion, 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

de plus, les variables doivent toujours être du côté droit de l'équation. cela le rend discutable.

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

DForck42
la source
1
Quelqu'un peut-il le corroborer à propos des variables de droite? Pour une raison quelconque, j'ai du mal à y croire.
Greg Dean
searchoracle.techtarget.com/expert/KnowledgebaseAnswer/ ... je ne trouve pas grand chose d'autre pour le moment
DForck42
Comme je comprends l'article. Il s'agit de fonctions sur les noms de colonnes qui ne sont pas sargables. Ce que je comprends. Cependant, je ne pense pas que (A = @a) ou (@a = A) compte.
Greg Dean
j'ai peut être tort. pourrait être une bonne question si elle n'existe pas déjà.
DForck42
1

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 aucun enregistrement. Une sorte de comportement de court-circuit était présent.

Alors essayé ceci:

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

savoir qu'aucun enregistrement ne 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.

Jorge
la source
1
Je suppose que la première requête a été "court-circuitée" au moment de la compilation, avant que l'exécution du plan ne démarre réellement.
Louis Somers
1

Voici une démonstration pour prouver que MySQL effectue le court-circuit de la clause WHERE :

http://rextester.com/GVE4880

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.

myslowfunctiondort délibérément pendant une seconde et a pour effet secondaire d'ajouter une entrée à une table de journal chaque fois qu'elle est exécutée. Voici les résultats de ce qui est consigné 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 plus de 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).

Steve Chambers
la source
4
Hmm ce que vous vouliez probablement dire "Voici une démonstration pour prouver que MySQL exécute le court-circuit de la clause WHERE dans ce cas particulier :"
TT.
1
Bien sûr, c'est juste une preuve que cela peut arriver.
Steve Chambers
0

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 moyen garanti!

bleuâtre
la source
-2

Il est évident que le serveur MS Sql prend en charge la théorie des courts-circuits, pour améliorer les performances en évitant les vérifications inutiles,

Exemple à l'appui:

SELECT 'TEST'
WHERE 1 = 'A'

SELECT 'TEST'
WHERE 1 = 1 OR 1 = 'A'

Ici, le premier exemple entraînerait l'erreur «La conversion a échoué lors de la conversion de la valeur varchar« A »en type de données int.»

Alors que la seconde s'exécute facilement car la condition 1 = 1 évaluée à TRUE et donc la seconde condition ne s'exécute pas du tout.

En outre

SELECT 'TEST'
WHERE 1 = 0 OR 1 = 'A'

ici, la première condition serait évaluée à faux et donc le SGBD irait pour la deuxième condition et vous obtiendrez à nouveau l'erreur de conversion comme dans l'exemple ci-dessus.

REMARQUE: J'ÉCRIT LA CONDITION ERRONEE JUSTE POUR RÉALISER LA MÉTÉO LA CONDITION EST EXÉCUTÉE OU EN COURT-CIRCUIT SI LA REQUÊTE RÉSULTAT EN ERREUR SIGNIFIE LA CONDITION EXÉCUTÉE, EN COURT-CIRCUIT AUTREMENT.

EXPLICATION SIMPLE

Considérer,

WHERE 1 = 1 OR 2 = 2

comme la première condition est évaluée à TRUE , il est inutile d'évaluer la deuxième condition car son évaluation dans n'importe quelle valeur n'affecterait pas du tout le résultat, donc c'est une bonne opportunité pour Sql Server de gagner du temps d'exécution de la requête en ignorant la vérification ou l'évaluation des conditions inutiles .

dans le cas de "OU" si la première condition est évaluée à TRUE, la chaîne entière connectée par "OR" serait considérée comme évaluée à true sans en évaluer les autres.

condition1 OR condition2 OR ..... OR conditionN

si la condition1 est évaluée à vrai, mettez toutes les conditions au repos jusqu'à ce que la conditionN soit ignorée. En termes généralisés lors de la détermination du premier TRUE , toutes les autres conditions liées par OR seraient sautées.

Considérez la deuxième condition

WHERE 1 = 0 AND 1 = 1

comme la première condition est évaluée à FALSE, il est inutile d'évaluer la deuxième condition car son évaluation dans n'importe quelle valeur n'affecterait pas du tout le résultat, donc encore une fois, c'est une bonne opportunité pour Sql Server de gagner du temps d'exécution de la requête en ignorant la vérification ou l'évaluation des conditions inutiles .

dans le cas de "ET" si la première condition est évaluée à FAUX, la chaîne entière connectée avec le "ET" serait considérée comme évaluée à FAUX sans en évaluer les autres.

condition1 AND condition2 AND ..... conditionN

si la condition1 est évaluée à FALSE , mettez toutes les conditions au repos jusqu'à ce que la conditionN soit ignorée. En termes généralisés lors de la détermination du premier FALSE , toutes les autres conditions liées par AND seraient ignorées.

PAR CONSÉQUENT, UN PROGRAMMEUR SAGE DOIT TOUJOURS PROGRAMMER LA CHAÎNE DE CONDITIONS DE TELLE FAÇON QUE, LA CONDITION LA MOINS CHER OU LA PLUS ÉLIMINATOIRE SOIT ÉVALUÉE D'ABORD, OU ARRANGER LA CONDITION DE TELLE FAÇON DE PRENDRE UN BÉNÉFICE MAXIMAL DU COURT-CIRCUIT

RkHirpara
la source
Raison du vote négatif: testez toujours les choses sur un vrai serveur avec des données réalistes. Il semble que mon commentaire précédent a été mangé.
Jasmine