Не дорівнює <>! = Оператор на NULL


271

Чи може хтось пояснити наступну поведінку в SQL?

SELECT * FROM MyTable WHERE MyColumn != NULL (0 Results)
SELECT * FROM MyTable WHERE MyColumn <> NULL (0 Results)
SELECT * FROM MyTable WHERE MyColumn IS NOT NULL (568 Results)

Відповіді:


309

<>є стандартним SQL-92; !=є його еквівалентом. Обидва оцінюють за значеннями, а NULLце не так - NULLце заповнювач, який стверджує, що немає значення.

Ось чому ви можете використовувати IS NULL/ IS NOT NULLяк предикати для таких ситуацій.

Така поведінка не характерна для SQL Server. Всі відповідні стандартам діалекти SQL працюють однаково.

Примітка . Для порівняння, якщо ваше значення не є нульовим , ви використовуєте IS NOT NULL, тоді як для порівняння з ненульовим значенням використовуєте <> 'YOUR_VALUE'. Я не можу сказати, чи моє значення дорівнює чи ні NULL, але я можу сказати, чи є моє значення NULL чи NOT NULL. Я можу порівняти, якщо моя вартість щось інше, ніж NULL.


4
Насправді, я вважаю, що він є <>в 92 специфікаціях, але більшість постачальників підтримує !=та / або він включається в більш пізню специфікацію, як 99 або 03.
Томас

2
@Thomas: Oracle не підтримував !=до ~ 9i, як я розумію, що принесло багато синтаксису ANSI-92. Я вважаю, що MySQL схожий, починаючи підтримку в 4.x.
OMG Ponies

Здається, це дозволяє припустити, що вона !=могла бути включена в більш пізню специфікацію як альтернатива <>. Не маю на руках нових специфікацій, тому я не можу сказати точно.
Томас

2
Це результат WHERE MyColumn != NULLчи WHERE MyColumn = NULLдетермінований? Або іншими словами, чи гарантовано завжди повертати 0 рядків, незалежно від того, MyColumnє нульовим в базі даних чи ні?
Слаума

11
Слід також зазначити, що оскільки !=лише оцінює значення, виконуючи щось на зразок WHERE MyColumn != 'somevalue', не повертаються записи NULL.
jsumrall

88

NULL не має значення, і тому його не можна порівняти за допомогою операторів скалярного значення.

Іншими словами, жодне значення ніколи не може бути рівним (або не дорівнює) NULL, оскільки NULL не має значення.

Отже, SQL має спеціальний IS NULL і НЕ NULL предикатів для роботи з NULL.


3
+1. І, всупереч заяві OP, це не "Microsoft SQL". Тринарна логіка визначена у стандарті SQL, а MS у цій точці дотримується стандарту.
TomTom

6
Я не припускав, що це лише поведінка Microsoft. Я просто заявив, що спостерігав це на Microsoft SQL Server.
Максим Гершкович

13
З інтересу, чи існують ситуації, коли ця (очікувана) поведінка корисна? Мені просто здається, що 'a' != nullНЕ повертає значення ( true/ 1) протилежно інтуїтивному і час від часу виловлює мене! Я б подумав, що "якесь значення порівняно з жодним значенням" завжди буде "не рівним", але, можливо, це тільки я?!?
DarthPablo

1
Я думаю, що цікаво, що люди описують NULL як " не має значення ". Точно так само, щоб сказати число 1 "має значення", коли воно насправді є значенням. Але NULL являє собою
нецінність

Як вирішення вручну, ви можете зазвичай SELECT * FROM MyTable WHERE coalesce(MyColumn, 'x') <> 'x'призначити константу, якщо це значення NULL, якщо ви надаєте відповідний тип даних для вартового значення x (у цьому випадку рядок / діаграма). Це синтаксис TSQL, але Oracle та інші двигуни мають схожі функції.
системний вирок

26

Зауважте, що така поведінка є типовою (ANSI) поведінкою.

Якщо ти:

 SET ANSI_NULLS OFF

http://msdn.microsoft.com/en-us/library/ms188048.aspx

Ви отримаєте різні результати.

SET ANSI_NULLS OFF мабуть, піде в майбутньому ...


8
+1 ... не скоро. Тепер, коли я можу отримати "дублікати" NULL в індексі? :(

Ви можете отримати дублікати NULL в індексі SQL Server, додавши пункт WHERE у відфільтрований індекс (наприклад create unique index UK_MyTable on MyTable (Column) where Column is not null): msdn.microsoft.com/en-us/library/cc280372.aspx
Anthony Mills

3
Примітка в документах: Якщо SET ANSI_NULLSзначення ВИМКНЕНО, оператори порівняння рівні (=) і не рівні (<>) не відповідають стандарту ISO. Оператор SELECT, який використовує, WHERE column_name = NULLповертає рядки, які мають нульові значення у колонці_імена. WHERE column_name <> NULLОператор SELECT, який використовує, повертає рядки, у яких стовпці не мають значення. Крім того, оператор SELECT, який використовує WHERE column_name <> XYZ_valueповертає всі рядки, які не є XYZ_value, і які не є NULL. ІМХО, це останнє твердження здається трохи дивним, оскільки це виключення нулів з результатів!
DarthPablo

4
Важливе зауваження від msdn doc : У майбутній версії SQL Server [новішої, ніж у 2014 році] ANSI_NULLS завжди буде ВКЛ. Уникайте використання цієї функції в нових роботах з розробки та плануйте змінювати додатки, які зараз використовують цю функцію.
Отьєль

7

У SQL все, що ви оцінюєте / обчислюєте за NULLрезультатами, НЕВІДОМЛЕНО

Ось чому SELECT * FROM MyTable WHERE MyColumn != NULLабо SELECT * FROM MyTable WHERE MyColumn <> NULLдає 0 результатів.

Для перевірки NULLзначень передбачена функція isNull.

Більше того, ви можете використовувати ISоператора, як ви використовували в третьому запиті.

Сподіваюсь, це допомагає.


"У SQL все, що ви оцінюєте / обчислюєте за результатами NULL, в 'NULL'" - невірно. Ви маєте на увазі результат НЕВІДОМО.
onedaywhen

@MahendraLiya функція isNull не передбачена для перевірки NULLS, але вона " Замінює NULL на вказане значення заміщення. " Ви повинні використовувати IS NULL або IS NULL замість ISNULL, що є різною річчю.
інженер, що

7

Єдиний тест на NULL - це NULL або NE NULL. Тестування на рівність є безглуздим, оскільки за визначенням ніхто не знає, що таке значення.

Ось стаття з Вікіпедії для читання:

https://en.wikipedia.org/wiki/Null_(SQL)


6

Ми використовуємо

SELECT * FROM MyTable WHERE ISNULL(MyColumn, ' ') = ' ';

щоб повернути всі рядки, де MyColumn - NULL, або всі рядки, де MyColumn - порожній рядок. Для багатьох "кінцевих споживачів" проблема NULL проти порожнього рядка - це відмінність без потреби та точки плутанини.


5

Я просто не бачу, що функціональна і безперебійна причина, коли нулі не можна порівняти з іншими значеннями або іншими нулями, тому що ми можемо чітко порівняти їх і сказати, що вони однакові чи ні в нашому контексті. Смішно. Просто через деякі логічні висновки та послідовність нам потрібно постійно турбуватися з цим. Це не функціонально, зробіть його більш функціональним і залиште це філософам та вченим, щоб вони зробили висновок, послідовний він чи ні, і чи дотримується він "загальної логіки". :) Хтось може сказати, що це через індекси чи щось інше, я сумніваюся, що ці речі не могли бути зроблені для підтримки нулів, таких як значення. Це те саме, що порівнювати два порожні келихи, один - келих для лози, а другий - пиво, ми не порівнюємо типи предметів, але значення, які вони містять, те саме, що можна порівняти з int та varchar, з нульовим. ' s що ще простіше, це нічого, і те, що мають дві спільності нічого, вони однакові, явно порівнянні мною і всіма іншими, хто пише sql, тому що ми постійно порушуємо цю логіку, порівнюючи їх дивними способами через деякі стандарти ANSI. Чому б не використати комп’ютерну потужність, щоб зробити це для нас, і я сумніваюся, що це сповільнить ситуацію, якщо все, що пов'язане, будуватиметься з цим на увазі. "Це не нуль, це нічого", це не яблуко, це apfel, давай ... Функціонально це твій друг, і тут теж є логіка. Зрештою, важливим є лише функціональність, а використання нулів таким чином приносить більшу чи меншу функціональність та простоту використання. Це корисніше? тому що ми постійно порушуємо цю логіку, порівнюючи їх дивними способами через деякі стандарти ANSI. Чому б не використати комп’ютерну потужність, щоб зробити це для нас, і я сумніваюся, що це сповільнить ситуацію, якщо все, що пов'язане, будуватиметься з цим на увазі. "Це не нуль, це нічого", це не яблуко, це apfel, давай ... Функціонально це твій друг, і тут теж є логіка. Зрештою, важливим є лише функціональність, а використання нулів таким чином приносить більшу чи меншу функціональність та простоту використання. Це корисніше? тому що ми постійно порушуємо цю логіку, порівнюючи їх дивними способами через деякі стандарти ANSI. Чому б не використати комп’ютерну потужність, щоб зробити це для нас, і я сумніваюся, що це сповільнить ситуацію, якщо все, що пов'язане, будуватиметься з цим на увазі. "Це не нуль, це нічого", це не яблуко, це apfel, давай ... Функціонально це твій друг, і тут теж є логіка. Зрештою, важливим є лише функціональність, а використання нулів таким чином приносить більшу чи меншу функціональність та простоту використання. Це корисніше? s не яблуко це apfel, давай ... Функціонально є твоїм другом, і тут теж є логіка. Зрештою, важливим є лише функціональність, а використання нулів таким чином приносить більшу чи меншу функціональність та простоту використання. Це корисніше? s не яблуко це apfel, давай ... Функціонально є твоїм другом, і тут теж є логіка. Зрештою, важливим є лише функціональність, а використання нулів таким чином приносить більшу чи меншу функціональність та простоту використання. Це корисніше?

Розглянемо цей код:

SELECT CASE WHEN NOT (1 = null or (1 is null and null is null)) THEN 1 ELSE 0 end

Хто з вас знає, що поверне цей код? З або без НЕ він повертає 0. Для мене це не функціонально і заплутано. У c # це все як належить, операції порівняння повертають значення, логічно, це теж дає значення, тому що якщо воно не було, то для порівняння немає нічого (крім нічого :)). Вони просто "сказали": все, порівняно з нульовим, "повертає" 0, і це створює багато обхідних ситуацій та головних болів.

Це код, який привів мене сюди:

where a != b OR (a is null and b IS not null) OR (a IS not null and b IS null)

Мені просто потрібно порівняти, якщо два поля (там, де) мають різні значення, я міг би використовувати функцію, але ...


4

NULL Не можна порівняти жодне значення за допомогою операторів порівняння. NULL = NULL помилково. Нуль не є цінністю. Оператор IS спеціально розроблений для обробки порівнянь NULL.


5
Мені завжди подобалось заплутаних людей, коли я іноді використовую те, null = nullде можна скористатися 1=0в якомусь спеціальному запиті. І якщо вони скаржаться, я зміню це на null != null:)
SWeko

8
"NULL = NULL хибно" Це не так. NULL = NULL оцінюється до невідомого та неправдивого.
nvogel

@dportas це так, але я мав на увазі, що в умовному випадку це не буде оцінено як істинне.
Вінсент Рамдані

@VincentRamdhanie не є хибним; насправді, у postgres це буде оцінено як NULL
Пере,

2

Старе питання, але наступне може запропонувати ще кілька деталей.

nullне представляє значення або невідоме значення. Він не вказує, чому немає значення, що може призвести до певної неоднозначності.

Припустимо, ви запускаєте такий запит:

SELECT *
FROM orders
WHERE delivered=ordered;

тобто ви шукаєте рядки, де orderedі deliveredдати однакові.

Що можна очікувати, коли один чи обидва стовпці є недійсними?

Оскільки принаймні одна дата невідома, не можна сподіватися сказати, що дві дати однакові. Це також той випадок, коли обидві дати невідомі: як вони можуть бути однаковими, якщо ми навіть не знаємо, що вони?

З цієї причини будь-який вираз, що трактується nullяк значення, повинен бути невдалим. У цьому випадку вона не збігатиметься. Це також має місце, якщо ви спробуєте наступне:

SELECT *
FROM orders
WHERE delivered<>ordered;

Знову ж таки, як можна сказати, що дві величини не є однаковими, якщо ми не знаємо, що вони є.

SQL має специфічний тест на відсутні значення:

IS NULL

Зокрема, вона не порівнює значення, а скоріше шукає пропущені значення.

Нарешті, що стосується !=оператора, наскільки я знаю, він насправді не є жодним із стандартів, але він дуже широко підтримується. Це було додано, щоб програмісти з деяких мов відчували себе як вдома. Відверто кажучи, якщо програмісту важко запам'ятати, якою мовою вони користуються, вони починають погано починати.


Це та сама "безглузда" "логіка", яку @Hove описує у своїй відповіді. Правда полягає в тому, що в цьому контексті немає потреби в цій додатковій атрибутиці; можна легко припустити, що коли ми порівнюємо щось з a, то NULLми маємо на увазі, що ми порівнюємо значення з "мати NULLзначення", а не значення з "невизначеним значенням, яке NULLмає нижнє підкладка" , але що ми не знаємо ", що, очевидно, ми не збираємося ніколи знати. Це дійсно полегшило б справи.
Пер

@Pere я б не сказав, що це суворо «безглуздо», і я не впевнений, що писати IS NULLнабагато складніше, ніж писати = NULL. Я думаю, що було б більш послідовно, якби це WHERE columnA = columnBбуло те саме тлумачення WHERE columnA = NULL, а не трактувати останню як особливий випадок. Пам'ятайте, що NULLце не цінність. У мовах програмування , де він є законним , щоб перевірити variable == nullце , тому що nullмає інше значення; це не являє собою щось невідоме, а навмисне скидання значення. Не так із SQL.
Манго

Ось чому я ставлю його між цитатами, @Mangoo;) (а також "логікою"). Не гнівайся на мене; Я говорив про "міркування" ANSI, а не про ваше пояснення. Я погоджуюся з тим, що між вами останнім прикладом немає накладних витрат між IS NULLAND =NULL. Але погляньте на останній Ховер. Я втомився відчувати це знову і знову, змушуючи робити зайві навантаження? додаткова перевірка ...
Пере,

1

Я хотів би запропонувати цей код, який я зробив, щоб дізнатися, чи є зміна значення, iє новим значенням і dє старим (хоча порядок не має значення). З цього приводу перехід від значення до нуля або навпаки - це зміна, але від нуля до нуля не є (звичайно, зі значення на інше значення - це зміна, але від значення до того ж не є).

CREATE FUNCTION [dbo].[ufn_equal_with_nulls]
(
    @i sql_variant,
    @d sql_variant
)
RETURNS bit
AS
BEGIN
    DECLARE @in bit = 0, @dn bit = 0
    if @i is null set @in = 1
    if @d is null set @dn = 1

    if @in <> @dn
        return 0

    if @in = 1 and @dn = 1
        return 1

    if @in = 0 and @dn = 0 and @i = @d
        return 1

    return 0

END

Використовувати цю функцію ви можете

declare @tmp table (a int, b int)
insert into @tmp values
(1,1),
(1,2),
(1,null),
(null,1),
(null,null)

---- in select ----
select *, [dbo].[ufn_equal_with_nulls](a,b) as [=] from @tmp

---- where equal ----
select *,'equal' as [Predicate] from @tmp where  [dbo].[ufn_equal_with_nulls](a,b) = 1

---- where not equal ----
select *,'not equal' as [Predicate] from @tmp where  [dbo].[ufn_equal_with_nulls](a,b) = 0

Результати:

---- in select ----
a   b   =
1   1   1
1   2   0
1   NULL    0
NULL    1   0
NULL    NULL    1

---- where equal ----
1   1   equal
NULL    NULL    equal

---- where not equal ----
1   2   not equal
1   NULL    not equal
NULL    1   not equal

Використання sql_variant робить його сумісним для різних типів


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.