НЕ (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 Противоположность «автомобиль красный и имеет четыре двери»: «Либо автомобиль не красный, либо у него нет четырех дверей». Если множественные вещи должны быть истинными, чтобы сделать утверждение истинным, то только одно из них должно быть ложным, чтобы сделать его ложным.
Хоббс
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.