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

16

W WHEREklauzuli zapytania SQL spodziewałbym się, że te dwa warunki będą miały takie samo zachowanie:

NOT (a=1 AND b=1)

vs

a<>1 AND b<>1

Pierwszy warunek zachowuje się zgodnie z oczekiwaniami i chociaż oczekuję, że drugi warunek robi to samo, tak nie jest.

To bardzo podstawowe rzeczy, ale wstydzę się, że nie widzę, co robię źle.

jon
źródło
Czy możesz zamieścić przykładowe dane i oczekiwane wyniki w porównaniu do faktycznych wyników?
Gareth Lyons,
6
Jak zauważył Lenard w swojej odpowiedzi, jest to przykład reguł De Morgana: nie (A i B) = (nie A) lub (nie B) , nie (A lub B) = (nie A) i (nie B) . Uważaj na wartości NULL.
Barranka
2
Pomyśl o tym po angielsku. Po pierwsze: „To nie jest tak, że jestem królem Francji, a także człowiekiem” - absolutnie prawda. Drugi to „Nie jestem ani królem Francji, ani człowiekiem” - zdecydowanie fałszywy.
Patrick Stevens,
3
Jest to sprzeczne z „prawem De Morgana”. Odpowiednikiem byłoby a <> 1 OR b<>1.
Willem Van Onsem,

Odpowiedzi:

46

Nie są równoważne.

NOT (a=1 AND b=1)

jest równoważne z:

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

Ta równoważność jest znana jako De Morgan's Law. Zobacz na przykład:

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

Fajną techniką sprawdzania / odrzucania równoważności wyrażeń algebry boolowskiej jest użycie cte dla domen i porównanie wyrażeń obok siebie:

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

Edycja: Ponieważ DB2 nie obsługuje logicznego typu danych, rozwinąłem przykład pod adresem:

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

Przepisane zapytanie wygląda następująco:

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;

Wynik zapytania jest następujący:

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)

Jak pokazano exp1 i exp2 są równoważne.

Lennart
źródło
16
+1 tylko za wspomnienie o De Morgan. Powinny być wymagane czytanie dla każdego, kto robi jakąkolwiek formę programowania / skryptów.
Tonny
Ale co z NULL?
dan04
@ dan04 Możesz dodać NULL do pierwszego wiersza (staje się with T(a) as ( values 0,1,NULL )i ponownie uruchom zapytanie, a zobaczysz, co się stanie. NULL zdecydowanie rzuci klucz w większość ustalonych zasad równoważności, których się uczymy. Krótka odpowiedź to = NULL i < > NULL oba dają NULL, więc przejdą do innego przypadku. Do dalszego czytania: ( stackoverflow.com/questions/1833949/… )
Brian J
Nie jestem pewien, dlaczego musiałeś zmienić pierwszy przykład dla DB2. Działa jak pokazano dla mnie. Używam DB2 for i zamiast DB2 LUW. Drugi przykład zawiera kilka błędów składniowych programu DB2 for i.
jmarkmurphy
@jmarkmurphy, nie znam DB2 for i, być może tam działa. W przypadku LUW wyrażenie na wielkość liter odwzorowuje na 0 lub 1, więc należy je zmienić, tak aby zawierało również null. W ten sposób wyrażenie przypadku nie jest już trywialne (IMO), a wyrażenia stają się trudne do uzasadnienia.
Lennart,
9

Twój pierwszy przykład mówi:

Zwraca wszystkie wiersze z wyjątkiem gdzie zarówno A = 1 i b = 1

Twój drugi przykład mówi:

Zwraca wszystkie wiersze z wyjątkiem , gdzie albo a = 1 OR B = 1

Aby drugie zapytanie zwróciło to samo co pierwsze, zmień swoje ANDnaOR

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

Zwraca to następujące wyniki

a   b
0   0
1   0
0   1
Mark Sinkinson
źródło
Czy mógłbyś opisać, dlaczego a<>1 AND b<>1tłumaczy się na „albo a = 1 LUB b = 1”?
doub1ejack
1
@ doub1ejack, trzeba dodatkowego negację w swoim drugim oświadczeniu zrobić to równoznaczne z pierwszym: NOT ( a=1 OR b=1 ). Niefortunne języki naturalne zawierają dwuznaczności, co utrudnia przetłumaczenie formuł logicznych na języki naturalne i odwrotnie. Na przykład neither a=1 nor b=1oznacza NOT ( a=1 OR b=1 )lub (NOT a=1) OR (NOT b=1)?
Lennart,
1
@ doub1ejack Przeciwieństwo „samochód jest czerwony ORAZ ma czworo drzwi” to „Albo samochód nie jest czerwony, LUB nie ma czterech drzwi”. Jeśli wiele rzeczy musi być prawdą, aby oświadczenie było prawdziwe, to tylko jedna z nich musi być fałszywa, aby uczynić je fałszywym.
hobbs