NOT (a = 1 AND b = 1) vs (a <> 1 AND b <> 1)

16

Na WHEREcláusula de uma consulta SQL, eu esperaria que essas duas condições tivessem o mesmo comportamento:

NOT (a=1 AND b=1)

vs

a<>1 AND b<>1

A primeira condição se comporta conforme o esperado e, embora eu preveja a segunda condição para fazer a mesma coisa, não.

Isso é muito básico, mas, com vergonha, não consigo ver o que estou fazendo de errado.

Jon
fonte
Você pode postar dados de exemplo e resultados esperados x resultados reais?
perfil completo de Gareth Lyons
6
Como observado por Lenard em sua resposta, este é um exemplo das regras de De Morgan: não (A e B) = (não A) ou (não B) , não (A ou B) = (não A) e (não B) . Cuidado com os valores NULL.
Barranka
2
Pense nisso em inglês. Seu primeiro é "Não é o caso de eu ser o rei da França e também humano" - eminentemente verdade. O seu segundo é "Eu não sou o rei da França nem humano" - eminentemente falso.
Patrick Stevens
3
Isso está em conflito com a "lei de De Morgan". O equivalente seria a <> 1 OR b<>1.
Willem Van Onsem

Respostas:

46

Eles não são equivalentes.

NOT (a=1 AND b=1)

é equivalente a:

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

Essa equivalência é conhecida como De Morgan's Law. Veja por exemplo:

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

Uma boa técnica para provar / refutar equivalências para expressões de álgebra booleana é usar um cte para os domínios e comparar as expressões lado a lado:

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: Como o DB2 não suporta o tipo de dados booleano, expandi o exemplo em:

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

A consulta reescrita se parece com:

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;

O resultado da consulta é:

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)

Como mostrado, exp1 e exp2 são equivalentes.

Lennart
fonte
16
+1 apenas por mencionar De Morgan. Deve ser leitura obrigatória para qualquer pessoa que faça qualquer forma de programação / script.
quer
Mas e quanto a NULL?
dan04
@ dan04 Você pode adicionar NULL à primeira linha (torna with T(a) as ( values 0,1,NULL )- se e executa novamente a consulta e verá o que acontece. NULLs definitivamente acionam uma chave na maioria das regras de equivalência definidas que aprendemos. Resposta curta é a = NULL e uma < > NULL produzem NULL, então eles caem para o caso else.Para uma leitura mais aprofundada: ( stackoverflow.com/questions/1833949/… )
Brian J #
Não sei por que você teve que alterar o primeiro exemplo do DB2. Funciona como mostrado para mim. Estou usando o DB2 para i em vez do DB2 LUW. O segundo exemplo possui alguns erros de sintaxe para o DB2 for i.
precisa saber é o seguinte
@markmark, eu não conheço o DB2 para i, talvez ele funcione lá. Para LUW, a expressão de caso é mapeada para 0 ou 1, de forma que também deve ser alterada para incluir nulo. Ao fazer isso, a expressão do caso não é mais trivial (IMO) e as expressões se tornam difíceis de raciocinar.
Lennart
9

Seu primeiro exemplo está dizendo:

Retornar todas as linhas , exceto onde tanto a = 1 AND b = 1

Seu segundo exemplo está dizendo:

Retornar todas as linhas , exceto onde quer a = 1 OR b = 1

Para que a segunda consulta retorne o mesmo que a primeira, você deve alterar ANDparaOR

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);

Isso retorna os seguintes resultados

a   b
0   0
1   0
0   1
Mark Sinkinson
fonte
Você poderia descrever por que se a<>1 AND b<>1traduz em "a = 1 OR b = 1"?
Duvida #
11
@ doub1ejack, você precisa de uma negação adicional em sua segunda declaração para torná-lo equivalente com o primeiro: NOT ( a=1 OR b=1 ). Línguas naturais infelizes contêm ambiguidades, o que dificulta a tradução de fórmulas lógicas para línguas naturais e vice-versa. Por exemplo, neither a=1 nor b=1significa NOT ( a=1 OR b=1 )ou (NOT a=1) OR (NOT b=1)?
Lennart
11
@ duplex1 O oposto de "o carro é vermelho E tem quatro portas" é "Ou o carro não é vermelho, OU não tem quatro portas". Se várias coisas precisam ser verdadeiras para tornar uma afirmação verdadeira, apenas uma delas precisa ser falsa para torná-la falsa.
Hbbs