Я решил проблему запроса с помощью ... row_number() over (partition by
... это более общий вопрос о том, почему мы не можем использовать столбцы с нулевыми значениями в соединениях. Почему ноль не может быть равен нулю ради объединения?
Я решил проблему запроса с помощью ... row_number() over (partition by
... это более общий вопрос о том, почему мы не можем использовать столбцы с нулевыми значениями в соединениях. Почему ноль не может быть равен нулю ради объединения?
Ответы:
Почему ноль не может быть равен нулю ради объединения?
Просто скажите 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)
В качестве альтернативы вы можете сделать два нулевых соответствия друг другу, используя 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.
Просто для полноты я упомяну, что 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 находится в разделе Основные материализованные представления - Выбор индексов для материализованных представлений .
Вы можете объединить нулевые значения, используя 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)
Почему вы не можете использовать нулевые значения в соединениях? В Oracle оба следующих значения не оцениваются как true:
NULL = NULL
NULL <> NULL
Вот почему мы имеем IS NULL
/ IS NOT NULL
для проверки значений NULL.
Чтобы проверить это, вы можете просто сделать:
SELECT * FROM table_name WHERE NULL = NULL
Объединения оценивают логическое состояние, и они не программировали их для работы по-другому. Вы можете поставить знак «больше» в условии соединения и добавить другие условия; он просто оценивает его как логическое выражение.
Я полагаю, что нуль не может быть равен нулю в соединениях ради согласованности. Это бросило бы вызов обычному поведению оператора сравнения.
NULL = anything
в результате, NULL
потому что стандарт SQL так говорит. Строка удовлетворяет условию соединения, только если выражение истинно.
Нулевое значение в большинстве реляционных баз данных считается НЕИЗВЕСТНЫМ. Это не следует путать со всеми шестнадцатеричными нулями. если что-то содержит нуль (неизвестно), вы не можете сравнить это.
Unknown = Known False
Unknown = Unknown False
Unknown >= Known False
Known >= Unknown False
Это означает, что всякий раз, когда в логическом выражении у вас есть нулевой операнд, остальная часть всегда будет истинной.
Вопреки общей ненависти разработчиков к null, у null есть свое место. Если что-то неизвестно, используйте нуль.
UNKNOWN
, не FALSE
;)
where (a = b or (a is null and b is null))
точка. Это мои мысли об этом. Я бы не стал использоватьsys_op_map_nonnull
, игнорируй этого человека за занавесом ".