Bezdepbonusu7Программирование: статьи и ответы

Вопрос

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

Автор вопроса: roubleОтветов: 9

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

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

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

nullnullablesqldatabaseinformix

Ответы

Ответ 1 · рейтинг 10 · Tom H

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

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, но посмотрите, что я сказал выше о стандартах...

Ответ 2 · рейтинг 3 · Donnie

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

Ответ 3 · рейтинг 1 · Adriaan Stander

Вы можете попробовать что-то подобное в 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

Ответ 4 · OMG Ponies

Если вы хотите быть уверены в том, как обрабатываются значения 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)

Ответ 5 · Gordon Bell

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

WHERE ISNULL(A, '') <> ISNULL(B, '')

Ответ 6 · bobince

Проблема в том, что 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 с его проблемой с пустой строкой...)

Ответ 7 · Jonathan Leffler

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'), как таковые.

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

Ответ 8 · user3830747

If

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

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

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

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

Ответ 9 · T.Z.

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

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

Перевод вопроса и ответов сообщества Stack Overflow, лицензия CC BY-SA. Оригинал на Stack Overflow.

Похожие материалы