тестирование неравенства со столбцами, которые могут быть нулевыми

Итак, сегодня утром я задал вопрос, что и сделал неправильно выразился, поэтому я получил много ответов о том, почему NULL по сравнению с чем-либо даст NULL/FALSE.

Мой фактический вопрос заключался в том, какова проверенная временем мода, в которой ребята из базы данных проверяют неравенства для двух столбцов, которые могут быть NULL. Мой вопрос прямо противоположен этому вопросу.

Требования следующие: A и B — два столбца:
а) если A и B оба равны NULL, они равны, вернуть FALSE
б) если A и B оба не NULL, вернуть A‹ >B
c) если A или B равны NULL, они не равны, вернуть TRUE


person rouble    schedule 02.12.2009    source источник


Ответы (9)


В зависимости от типа данных и возможных значений столбцов:

COALESCE(A, -1) <> COALESCE(B, -1)

Хитрость заключается в том, чтобы найти значение (здесь я использовал -1), которое НИКОГДА не появится в ваших данных.

Другой способ:

(A <> B) OR (A IS NOT NULL AND B IS NULL) OR (A IS NULL AND B IS NOT NULL)

Это может быть проблемой в зависимости от того, как ваша конкретная СУБД обрабатывает NULL. По стандарту ANSI это должно дать вам то, что вы хотите, но кто в любом случае следует стандартам. :)

P.S. - Я также должен указать, что использование функции COALESCE может сделать недействительным использование индексов при сравнении столбцов. Проверьте свой план запроса и производительность запроса, чтобы убедиться, что это проблема.

П.П.С. - Я только что заметил, что OMG Ponies упомянули, что Informix не поддерживает COALESCE. Я считаю, что это стандартная функция ANSI, но посмотрите, что я сказал выше о стандартах...

person Tom H    schedule 02.12.2009
comment
@Tom: Не то чтобы это не поддерживалось, просто это, похоже, зависит от версии/выпуска. - person OMG Ponies; 02.12.2009
comment
И обертывание столбца в любую функцию означает, что индекс не используется, если он есть. - person OMG Ponies; 02.12.2009
comment
Запрос на объединение не работает, если -1 является допустимым значением для столбца. Если я закодирую это так, и сегодня -1 не является допустимым значением, а позже оно станет допустимым значением, это будет ошибка, когда нулевое поле равно полю -1. - person rouble; 02.12.2009
comment
COALESCE() отлично подходит для этой цели - person Josh Smeaton; 02.12.2009
comment
@OMG Ponies - Спасибо за разъяснения. Что касается функции, аннулирующей индексы, я упоминал об этом выше в моем P.S. - person Tom H; 02.12.2009
comment
@prmatta - Вот почему я использовал все заглавные буквы жирным шрифтом, чтобы никогда. Лично я использую второй формат именно по этой причине (и из-за проблем с индексацией) при кодировании хранимых процедур и т. д. - person Tom H; 03.12.2009
comment
@prmatta & Tom: Просто вопрос выбора контрольного значения - значения, которое никогда не будет встречаться в данных. - person OMG Ponies; 03.12.2009
comment
Рад слышать, что смог помочь :) - person Tom H; 03.12.2009

Я бы лично записал выражение, которое вы придумали, особенно если ожидается, что таблица станет большой. Оборачивание столбцов в вызовы функций снижает производительность, так как движок не может использовать какие-либо индексы, которые у вас есть для этих столбцов. Конечно, в маленькой таблице это может не быть проблемой, но мне все же нравится делать это явным образом на случай, если таблица в конечном итоге будет расти.

person Donnie    schedule 02.12.2009

Вы можете попробовать что-то подобное в Informix?

CASE
    WHEN a IS NULL AND B IS NULL THEN false 
    WHEN a IS NULL OR B IS NULL THEN true
    ELSE a <> B
END

из Руководство IBM Informix по SQL: синтаксис, выражения CASE

person Adriaan Stander    schedule 02.12.2009
comment
Да, CASE-WHEN, COALESCE и NULLIF — все это ANSI SQL92 и работает в WHERE. - person bobince; 03.12.2009

Если вы хотите быть уверены в том, как обрабатываются значения NULL, вам придется использовать все, что Informix поддерживает для проверки значений NULL. Я не нашел многого, за исключением того, что версия SE не поддерживает COALESCE, но поддерживает DECODE и, возможно, CASE.

WHERE COALESCE(t.a, 0) != COALESCE(t.b, 0)
WHERE DECODE(NULL, 0, t.a) != DECODE(NULL, 0, t.b)
person OMG Ponies    schedule 02.12.2009
comment
Объединение с 0, вероятно, плохо, так как будет равно false. - person Josh Lee; 02.12.2009
comment
@jleedev: Требование состоит в том, чтобы a и b не были равны друг другу. - person OMG Ponies; 02.12.2009
comment
Если одно 0, а другое NULL, то вы получите 0 != 0, что ложно там, где должно быть истинным. - person Josh Lee; 02.12.2009
comment
@jleedev: Это пример - мы не знаем, что такое тип данных. - person OMG Ponies; 02.12.2009

Для SQL Server используйте:

WHERE ISNULL(A, '') <> ISNULL(B, '')
person Gordon Bell    schedule 02.12.2009

Проблема в том, что a<>b (или a=b) дает NULL, а не 1 или 0, когда один или оба операнда равны NULL. Это не имеет значения для случая =, потому что NULL OR 1 — это 1, а NULL OR 0 — это NULL, который ведет себя как 0 при выборе в предложении WHERE.

Ты мог бы сказать:

a<>b OR (a IS NULL)<>(b IS NULL)

Однако необходимость сделать это в любом случае может быть признаком того, что вы неправильно используете NULL, и вам следует подумать об изменении схемы, чтобы использовать какое-то другое значение NOT NULL для обозначения этого сопоставимого условия.

Например, если у вас есть таблица person со столбцом title, не используйте NULL, чтобы показать, что у них нет заголовка; это не «отсутствующие» данные, просто названия не существует. Так что сохраните его как пустую строку '', которую вы можете легко сравнить с другими пустыми строками. (Ну, если вы, конечно, не запускаете Oracle с его проблемой с пустой строкой...)

person bobince    schedule 02.12.2009
comment
На самом деле мой запрос не работает. Если только один из A или B равен NULL, мой запрос вернет FALSE, что неверно. - person rouble; 03.12.2009
comment
Ах... на самом деле не 0 (false), он возвращает NULL из-за сравнения... но тогда в случае равенства это не имеет значения, а в случае неравных имеет значение. Редактирование... - person bobince; 03.12.2009
comment
@bobince ваше решение очень элегантное и правильное, однако оно дает синтаксическую ошибку в informix 11.5. - person rouble; 03.12.2009
comment
Действительно? В чем ошибка? Для меня это выглядит как действительный ANSI SQL: 1992, если только я не пьян и что-то не пропустил. не то, чтобы я когда-либо был пьян от SO, очевидно. Почти никогда. Кроме иногда. Но в основном нет. - person bobince; 04.12.2009
comment
Разве следующее не работает для Informix? где a ‹› b или nvl(a, 't') ‹› nvl(b, 't') - person calvinkrishy; 16.12.2009

IBM Informix Dynamic Server имеет несколько своеобразный взгляд на булевы значения по целому ряду исторических («плохих») причин. Адаптируя идею, предложенную @astander, это выражение CASE «работает», но я буду первым, кто скажет «неочевидно» (видите, я сказал это раньше, чем вы!). Этап настройки:

create table x(a int, b int);
insert into x values(null, null);
insert into x values(null, 1);
insert into x values(1, null);
insert into x values(1, 1);
insert into x values(1, 2);

Оператор SELECT:

SELECT *
  FROM x
  WHERE   CASE
          WHEN a IS NULL AND b IS NULL THEN 'f'::BOOLEAN
          WHEN a IS NULL OR  b IS NULL THEN 't'::BOOLEAN
          WHEN a != b                  THEN 't'::BOOLEAN
          ELSE                              'f'::BOOLEAN
          END
;

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

                 1
      1           
      1          2

Проблемы:

  • IDS не распознает FALSE, TRUE или UNKNOWN как ключевые слова.
  • IDS не распознает логические выражения, такие как 'a != b' (или 'a ‹> b'), как таковые.

Да, мне очень больно говорить об этом.

person Jonathan Leffler    schedule 03.12.2009

If

where ((A=B) OR (A IS NULL AND B IS NULL))

для равенства, то почему бы просто не использовать:

where NOT (
  ((A=B) OR (A IS NULL AND B IS NULL))
)

за неравенство?

person user3830747    schedule 04.02.2016

Небольшая модификация ответа @user3830747 на основе закон Деморганов:

NOT (NVL(a = b,FALSE) OR COALESCE(a,b) IS NULL)

person T.Z.    schedule 28.04.2021