Чому я не можу використовувати нульові значення при приєднанні?


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)

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 приведені в розділі Basic Materialized Views - Choice Indexes for Materialized Views .


Так це задокументовано зараз? Тому що AskTom (2003 р.) Заявив: " - це незадокументоване, а отже, є ризик відійти або змінити функціональність, що достатньо сказано, що люди повинні просто" перестати читати "туди, і ви, можливо, справді злитеся в наступному випуску. єдиний ПРАВИЛЬНИЙ спосіб - це where (a = b or (a is null and b is null)) період. Це мої думки щодо цього. Я б не розглядав питання використання sys_op_map_nonnull, ігнорував би цю людину за завісою ".
ypercubeᵀᴹ

Якщо у вас є посилання, будь ласка, додайте його до питання. Я не знайшов згадки у функціях 12c, але пошук документації Oracle та конкретної версії досить важкий.
ypercubeᵀᴹ

2

Ви можете приєднати нульові значення за допомогою декодування:

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 обидва з перерахованих нижче не оцінюють справжнє:

  • NULL = NULL
  • NULL <> NULL

Ось чому ми маємо IS NULL/ IS NOT NULLперевірити наявність нульових значень.
Щоб перевірити це, ви можете просто зробити:

SELECT * FROM table_name WHERE NULL = NULL

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

Я думаю, що нуль не може бути рівним нулю в з'єднанні заради послідовності. Це б заперечувало звичайну поведінку оператора порівняння.


NULL = anythingпризводить до того, NULLщо стандарт SQL так говорить. Рядок задовольняє умові приєднання, лише якщо вираз є істинним.
Лоренц Albe

1
Поза буквальною деталізацією реалізації (що не завжди буває так: деякі БД мають можливість прирівняти NULL до NULL для деяких / всіх цілей) є логічна причина: NULL невідома. Коли ви порівнюєте NULL з NULL, ви запитуєте "чи ця невідома річ дорівнює цій іншій невідомій речі", на яку єдиною розумною відповіддю є "невідомо" - інший NULL (який відображається на помилковому в ситуації порівняння).
Девід Спіллетт

-4

Нульове значення в більшості реляційних баз даних вважається НЕЗАЄМО. Це не слід плутати з усіма нулями HEX. якщо щось містить null (невідомо,), ви не можете його порівняти.

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

Що означає, що коли ви матимете нуль як операнда в бульному виразі, інша частина завжди буде правдою.

На відміну від загальної ненависті розробників до нуля, null має своє місце. Якщо щось невідоме, використовуйте null.


6
Насправді всі приклади порівнянь у вас є, урожай UNKNOWN, ні FALSE;)
ypercubeᵀᴹ

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