НЕ (a = 1 AND b = 1) vs (a <> 1 AND 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 не підтримує тип даних Boolean, я розширив приклад на:

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 лише для згадки про Де Моргана. Потрібно прочитати всіх, хто робить будь-яку форму програмування / сценаріїв.
Тонні

А як же НУЛЛ?
dan04

@ dan04 Ви можете додати NULL до першого рядка (стає with T(a) as ( values 0,1,NULL )і повторно запустіть запит, і ви побачите, що відбувається. NULL неодмінно кидають гайковий ключ у більшості встановлених нами правил еквівалентності. Коротка відповідь - = NULL і a < > NULL обидва дають NULL, тому вони потраплять до іншого випадку. Для подальшого читання: ( stackoverflow.com/questions/1833949/… )
Brian J

Я не впевнений, чому вам довелося внести зміни до першого прикладу для DB2. Це працює, як показано для мене. Я використовую DB2 для i, а не DB2 LUW. Другий приклад містить деякі синтаксичні помилки для DB2 для i.
jmarkmurphy

@jmarkmurphy, я не знаю DB2 для i, можливо, він там працює. Для LUW вираз випадок відображається на 0 або 1, так що їх також потрібно змінити, щоб вони також включали null. Тим самим виразний випадок вже не є тривіальним (ІМО), і виразів стає важко обґрунтувати.
Леннарт

9

Ваш перший приклад:

Повертайте всі рядки, за винятком випадків, коли обидва a = 1 AND b = 1

Ваш другий приклад:

Повернення всіх рядків , крім , де або а = 1 OR B = 1

Щоб другий запит повертався таким же, як і перший, слід змінити свій ANDна anOR

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"?
sum1ejack

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
@ sum1ejack Протилежність "автомобіль червоний І має чотири двері" - "Або машина не червона, АБО не має чотирьох дверей". Якщо декілька речей повинні бути правдивими, щоб зробити заяву правдивою, тоді лише одна з них повинна бути хибною, щоб зробити її помилковою.
варення
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.