PAS (a = 1 ET b = 1) vs (a <> 1 ET b <> 1)

16

Dans la WHEREclause d'une requête SQL, je m'attendrais à ce que ces deux conditions aient le même comportement:

NOT (a=1 AND b=1)

contre

a<>1 AND b<>1

La première condition se comporte comme prévu, et bien que j'exige que la seconde condition fasse la même chose, ce n'est pas le cas.

C'est un truc très basique, mais honteusement je ne vois pas ce que je fais mal.

Jon
la source
Pouvez-vous publier des exemples de données et de résultats attendus par rapport aux résultats réels?
Gareth Lyons
6
Comme l'a noté Lenard dans sa réponse, ceci est un exemple de règles De Morgan: pas (A et B) = (pas A) ou (pas B) , pas (A ou B) = (pas A) et (pas B) . Soyez prudent avec les valeurs NULL.
Barranka
2
Pensez-y juste en anglais. Votre premier est «Ce n'est pas le cas que je sois à la fois le roi de France et aussi humain» - éminemment vrai. Votre deuxième est "je ne suis ni le roi de France ni humain" - éminemment faux.
Patrick Stevens
3
Cela entre en conflit avec la "loi De Morgan". L'équivalent serait a <> 1 OR b<>1.
Willem Van Onsem

Réponses:

46

Ils ne sont pas équivalents.

NOT (a=1 AND b=1)

est équivalent à:

(NOT a=1 OR NOT b=1) <=> (a<>1 OR b<>1)

Cette équivalence est connue sous le nom de De Morgan's Law . Voir par exemple:

https://en.wikipedia.org/wiki/De_Morgan%27s_laws

Une bonne technique pour prouver / infirmer les équivalences des expressions d'algèbre booléenne consiste à utiliser un cte pour les domaines et à comparer les expressions côte à côte:

with T(a) as ( values 0,1 )
   , U(a,b) as (select t1.a, t2.a as b 
               from t as t1 
               cross join t as t2
) 
select a,b
    , case when not (a=1 and b=1) then 1 else 0 end
    , case when a<>1 and b<>1 then 1 else 0 end 
from U

A           B           3           4          
----------- ----------- ----------- -----------
          0           0           1           1
          0           1           1           0
          1           0           1           0
          1           1           0           0

Edit: DB2 ne prenant pas en charge le type de données booléen, j'ai développé l'exemple à:

http://sqlfiddle.com/#!15/25e1a/19

La requête réécrite ressemble à:

with T(a) as ( values (0),(1),(null) )
   , U(a,b) as (select t1.a, t2.a as b 
                from t as t1 
                cross join t as t2
) 
select a,b
     , not (a=1 and b=1) as exp1 
     , a<>1 or b<>1 as exp2
from U;

Le résultat de la requête est:

a       b       exp1        exp2
--------------------------------
0       0       true        true
0       1       true        true
0       (null)  true        true
1       0       true        true
1       1       false       false
1       (null)  (null)      (null)
(null)  0       true        true
(null)  1       (null)      (null)
(null)  (null)  (null)      (null)

Comme indiqué, exp1 et exp2 sont équivalents.

Lennart
la source
16
+1 juste pour avoir mentionné De Morgan. Devrait être une lecture obligatoire pour toute personne faisant n'importe quelle forme de programmation / script.
Tonny
Mais qu'en est-il de NULL?
dan04
@ dan04 Vous pouvez ajouter NULL à la première ligne (devient with T(a) as ( values 0,1,NULL )et réexécutez la requête et vous verrez ce qui se passe. Les NULL jettent définitivement une clé dans la plupart des règles d'équivalence définies que nous apprenons. La réponse courte est a = NULL et a < > NULL donnent tous les deux NULL, donc ils tomberont dans le cas else. Pour plus d'informations: ( stackoverflow.com/questions/1833949/… )
Brian J
Je ne sais pas pourquoi vous avez dû modifier le premier exemple pour DB2. Cela fonctionne comme indiqué pour moi. J'utilise DB2 pour i plutôt que DB2 LUW. Le deuxième exemple contient des erreurs de syntaxe pour DB2 pour i.
jmarkmurphy
@jmarkmurphy, je ne connais pas DB2 pour i, peut-être que ça marche là-bas. Pour LUW, l'expression de casse est mappée sur 0 ou 1, ce qui doit être modifié pour inclure également null. Ce faisant, l'expression de cas n'est plus triviale (OMI) et les expressions deviennent difficiles à raisonner.
Lennart
9

Votre premier exemple dit:

Retour toutes les lignes , sauf lorsque les deux a = 1 ET b = 1

Votre deuxième exemple dit:

Retour toutes les lignes , saufsoit a = 1 OU b = 1

Pour que la deuxième requête renvoie la même chose que la première, vous devez remplacer votre ANDparOR

CREATE TABLE #Test (a BIT, b BIT);

INSERT INTO #Test
        ( a, b )
VALUES
        ( 0, 0 ),
        ( 1, 0 ),
        ( 0, 1 ),
        ( 1, 1 );

SELECT * FROM #Test AS t
WHERE NOT (a=1 AND b=1);

SELECT * FROM #Test AS t
WHERE (a <> 1 OR b <> 1);

Cela renvoie les résultats suivants

a   b
0   0
1   0
0   1
Mark Sinkinson
la source
Pourriez-vous décrire pourquoi se a<>1 AND b<>1traduit par "soit a = 1 OU b = 1"?
doub1ejack
1
@ doub1ejack, vous avez besoin d' une négation supplémentaire dans votre deuxième déclaration pour le rendre équivalent à la première: NOT ( a=1 OR b=1 ). Les langues naturelles malheureuses contiennent des ambiguïtés qui rendent difficile la traduction de formules logiques en langues naturelles et vice versa. Par exemple, neither a=1 nor b=1signifie NOT ( a=1 OR b=1 )ou (NOT a=1) OR (NOT b=1)?
Lennart
1
@ doub1ejack L'opposé de "la voiture est rouge ET a quatre portes" est "Soit la voiture n'est pas rouge, soit elle n'a pas quatre portes." Si plusieurs choses doivent être vraies pour qu'une déclaration soit vraie, alors une seule doit être fausse pour la rendre fausse.
hobbs