НЕ (a = 1 И b = 1) против (a <> 1 И b <> 1)

16

В WHEREпредложении SQL-запроса я ожидаю, что эти два условия будут иметь одинаковое поведение:

NOT (a=1 AND b=1)

против

a<>1 AND b<>1

Первое условие ведет себя так, как и ожидалось, и, хотя я и предполагаю, что второе условие делает то же самое, оно не выполняется

Это очень простые вещи, но стыдно, я не вижу, что я делаю неправильно.

джон
источник
Можете ли вы опубликовать пример данных и ожидаемых результатов по сравнению с фактическими результатами?
Гарет Лайонс
6
Как отметил Ленард в своем ответе, это пример правил де Моргана: не (A и B) = (не A) или (не B) , не (A или B) = (не A) и (не B) , Будьте осторожны со значениями NULL.
Барранка
2
Просто подумай об этом на английском. Ваше первое: «Это не тот случай, когда я и король Франции, и человек», - в высшей степени верно. Ваш второй «я не король Франции и не человек» - в высшей степени ложный.
Патрик Стивенс
3
Это противоречит «закону де Моргана». Эквивалент будет a <> 1 OR b<>1.
Виллем Ван Онсем

Ответы:

46

Они не эквивалентны.

NOT (a=1 AND b=1)

эквивалентно с:

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

Эта эквивалентность известна как De Morgan's Law. Смотрите, например:

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

Хорошим методом доказательства / опровержения эквивалентностей для выражений булевой алгебры является использование cte для доменов и сравнение выражений рядом друг с другом:

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

Изменить: Поскольку DB2 не поддерживает логический тип данных, я расширил пример на:

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

Переписанный запрос выглядит так:

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;

Результат запроса:

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)

Как показано, exp1 и exp2 эквивалентны.

Леннарт
источник
16
+1 только за упоминание Де Моргана. Требуется чтение для всех, кто занимается программированием / написанием сценариев.
Тонни
Но как насчет NULL?
17
@ dan04 Вы можете добавить NULL в первую строку (создайте with T(a) as ( values 0,1,NULL )и повторно запустите запрос, и вы увидите, что произойдет. NULL определенно выбрасывает ключ в большинстве изучаемых нами правил эквивалентности множеств. Краткий ответ a = NULL и a < > NULL оба дают NULL, поэтому они перейдут к другому случаю. Для дальнейшего чтения: ( stackoverflow.com/questions/1833949/… )
Брайан Дж
Я не уверен, почему вы должны были изменить первый пример для DB2. Это работает, как показано для меня. Я использую DB2 для меня, а не DB2 LUW. Во втором примере есть некоторые синтаксические ошибки для DB2 for i.
jmarkmurphy
@jmarkmurphy, я не знаю DB2 для меня, возможно, он там работает. Для LUW выражение case отображается либо в 0, либо в 1, поэтому необходимо изменить также на null. Таким образом, выражение case больше не является тривиальным (IMO), и выражения становятся трудно рассуждать.
Леннарт
9

Ваш первый пример говорит:

Возврат всех строк за исключением того, где и а = 1 и B = 1

Ваш второй пример говорит:

Возврат всех строк , кроме , где либо а = 1 OR B = 1

Чтобы второй запрос возвращал то же, что и первый, вы должны изменить ANDего наOR

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

Это возвращает следующие результаты

a   b
0   0
1   0
0   1
Марк Синкинсон
источник
Не могли бы вы описать, почему a<>1 AND b<>1переводится как «либо a = 1 ИЛИ b = 1»?
doub1ejack
1
@ doub1ejack, вам потребуется дополнительное отрицание в своем втором заявлении , чтобы сделать его эквивалент с первым: NOT ( a=1 OR b=1 ). Прискорбные естественные языки содержат неоднозначности, которые затрудняют перевод логических формул на естественные языки и наоборот. Например, neither a=1 nor b=1значит NOT ( a=1 OR b=1 )или (NOT a=1) OR (NOT b=1)?
Леннарт
1
@oub1ejack Противоположность «автомобиль красный и имеет четыре двери»: «Либо автомобиль не красный, либо у него нет четырех дверей». Если множественные вещи должны быть истинными, чтобы сделать утверждение истинным, то только одно из них должно быть ложным, чтобы сделать его ложным.
Хоббс