Почему я не могу использовать нулевые значения в соединениях?

13

Я решил проблему запроса с помощью ... row_number() over (partition by... это более общий вопрос о том, почему мы не можем использовать столбцы с нулевыми значениями в соединениях. Почему ноль не может быть равен нулю ради объединения?

Марк Уорнер
источник

Ответы:

31

Почему ноль не может быть равен нулю ради объединения?

Просто скажите Oracle, чтобы сделать это:

select *
from one t1 
  join two t2 on coalesce(t1.id, -1) = coalesce(t2.id, -1);

(Обратите внимание, что в стандартном SQL вы могли бы использовать t1.id is not distinct from t2.idнулевой оператор равенства, но Oracle не поддерживает это)

Но это будет работать только в том случае, если значение замены (-1 в приведенном выше примере) фактически не отображается в таблице. Поиск такого «магического» значения для чисел может быть возможным, но это будет очень трудно для символьных значений (особенно потому, что Oracle также обрабатывает пустую строку null)

Плюс: индекс по idстолбцам не будет использоваться ( хотя вы можете определить индекс на основе функции с помощью coalesce()выражения).

Еще один вариант, который работает для всех типов, без магических значений:

              on t1.id = t2.id or (t1.id is null and t2.id is null)

Но реальный вопрос: имеет ли это смысл?

Рассмотрим следующие примеры данных:

Стол первый

id
----
1
2
(null)
(null)

Таблица два

id
----
1
2
(null)
(null)
(null)

Какую из комбинаций нулевых значений следует выбрать в соединении? Приведенный выше пример приведет к перекрестному соединению для всех нулевых значений.

T1_ID  | T2_ID 
-------+-------
     1 |      1
     2 |      2
(null) | (null)
(null) | (null)
(null) | (null)
(null) | (null)
(null) | (null)
(null) | (null)
a_horse_with_no_name
источник
6

В качестве альтернативы вы можете сделать два нулевых соответствия друг другу, используя INTERSECTв качестве оператора равенства:

SELECT
  *
FROM
  t1
  INNER JOIN t2
    ON EXISTS (SELECT t1.ID FROM DUAL INTERSECT SELECT t2.ID FROM DUAL)
;

Смотрите эту демонстрацию DBFiddle для иллюстрации.

Конечно, это выглядит довольно скучно, хотя на самом деле это не намного дольше, чем предлагает BriteSponge . Тем не менее, это, безусловно, не соответствует, если вы простите за каламбур, лаконичности упомянутого ранее в комментариях стандартного способа, которым является IS NOT DISTINCT FROMоператор, еще не поддерживаемый в Oracle.

Андрей М
источник
2

Просто для полноты я упомяну, что SYS_OP_MAP_NONNULLтеперь функцию можно безопасно использовать для сравнения значений, которые являются нулевыми, как это теперь задокументировано в документации 12c. Это означает, что Oracle не будет просто случайно удалять его и нарушать ваш код.

SELECT *
FROM   one t1 
       JOIN two t2
         ON SYS_OP_MAP_NONNULL(t1.id) = SYS_OP_MAP_NONNULL(t2.id)

Преимущество в том, что вы не сталкиваетесь с проблемой «магического числа».

Ссылка в документации Oracle находится в разделе Основные материализованные представления - Выбор индексов для материализованных представлений .

BriteSponge
источник
Так это сейчас задокументировано? Потому что AskTom (в 2003 году) заявил: « - он недокументирован, и, следовательно, представляет собой риск уйти или изменить функциональность , достаточно сказать, что это заставит людей просто« перестать читать », и в следующем выпуске вы, возможно, по-настоящему злитесь». единственный ПРАВИЛЬНЫЙ способ: where (a = b or (a is null and b is null)) точка. Это мои мысли об этом. Я бы не стал использовать sys_op_map_nonnull, игнорируй этого человека за занавесом ".
ypercubeᵀᴹ
Если у вас есть ссылка, пожалуйста, добавьте ее к вопросу. Я не нашел упоминания в функциях 12c, но поиск документации и конкретной версии Oracle довольно сложен.
ypercubeᵀᴹ
2

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

on decode(t1.id, t2.id, 1, 0) = 1

decodeрассматривает нули как равные, так что это работает без "магических" чисел. Два столбца должны иметь одинаковый тип данных.

Это не сделает наиболее читаемый код, но, вероятно, все еще лучше, чем t1.id = t2.id or (t1.id is null and t2.id is null)

Тамаш Бараш
источник
1

Почему вы не можете использовать нулевые значения в соединениях? В Oracle оба следующих значения не оцениваются как true:

  • NULL = NULL
  • NULL <> NULL

Вот почему мы имеем IS NULL/ IS NOT NULLдля проверки значений NULL.
Чтобы проверить это, вы можете просто сделать:

SELECT * FROM table_name WHERE NULL = NULL

Объединения оценивают логическое состояние, и они не программировали их для работы по-другому. Вы можете поставить знак «больше» в условии соединения и добавить другие условия; он просто оценивает его как логическое выражение.

Я полагаю, что нуль не может быть равен нулю в соединениях ради согласованности. Это бросило бы вызов обычному поведению оператора сравнения.

Андрей Пульонези
источник
NULL = anythingв результате, NULLпотому что стандарт SQL так говорит. Строка удовлетворяет условию соединения, только если выражение истинно.
Лоренц Альб
1
Помимо буквальной детализации реализации (что не всегда так: некоторые БД имеют возможность приравнять NULL к NULL для некоторых / всех целей), существует логическая причина: NULL неизвестен. Когда вы сравниваете NULL с NULL, вы спрашиваете «является ли эта неизвестная вещь равной этой другой неизвестной вещи», на которую единственным разумным ответом является «неизвестный» - другой NULL (который сопоставляется с ложным в ситуации сравнения).
Дэвид
-4

Нулевое значение в большинстве реляционных баз данных считается НЕИЗВЕСТНЫМ. Это не следует путать со всеми шестнадцатеричными нулями. если что-то содержит нуль (неизвестно), вы не можете сравнить это.

Unknown = Known False
Unknown = Unknown False
Unknown >= Known False
Known >= Unknown False

Это означает, что всякий раз, когда в логическом выражении у вас есть нулевой операнд, остальная часть всегда будет истинной.

Вопреки общей ненависти разработчиков к null, у null есть свое место. Если что-то неизвестно, используйте нуль.

Jujiro
источник
6
На самом деле все примеры сравнения у вас есть, выход UNKNOWN, не FALSE;)
ypercubeᵀᴹ
Вы правы, однако цель логического выражения состоит в том, чтобы приводить только true или false, поэтому давайте не будем сходить с ума здесь :).
Jujiro