SQL "Join" для нулевых значений - PullRequest
21 голосов
/ 18 ноября 2009

По независящим от меня причинам мне нужно объединить две таблицы, и мне нужны нулевые значения для соответствия. Лучший вариант, который я мог придумать, - это выложить UUID и использовать его в качестве значения для сравнения, но это выглядит некрасиво

SELECT * FROM T1 JOIN T2 ON nvl(T1.SOMECOL,'f44087d5935dccbda23f71f3e9beb491') = 
   nvl(T2.SOMECOL,'f44087d5935dccbda23f71f3e9beb491')

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

Редактировать: Для ясности, я не только , связанный с нулями. Если столбцы не равны NULL, я хочу, чтобы они соответствовали фактическим значениям.

Ответы [ 12 ]

39 голосов
/ 18 ноября 2009

Возможно, это сработает, но я никогда не пробовал:

SELECT * 
FROM T1 JOIN T2 
ON T1.SOMECOL = T2.SOMECOL OR (T1.SOMECOL IS NULL AND T2.SOMECOL IS NULL)
6 голосов
/ 18 ноября 2009

В SQL Server я использовал:

WHERE (a.col = b.col OR COALESCE(a.col, b.col) IS NULL)

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

3 голосов
/ 04 августа 2011

Простой, используйте COALESCE, который вернет свой первый ненулевой параметр:

SELECT * FROM T1 JOIN T2 ON 
  COALESCE(T1.Field, 'magic string') = 
     COALESCE(T2.Field, 'magic string')

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

3 голосов
/ 19 ноября 2009

Для такого рода задач Oracle внутренне использует недокументированную функцию sys_op_map_nonnull (), где ваш запрос станет:

SELECT *
FROM T1 JOIN T2 ON sys_op_map_nonnull(T1.SOMECOL) = sys_op_map_nonnull(T2.SOMECOL)

Недокументированное, поэтому будьте осторожны, если вы идете по этому маршруту.

3 голосов
/ 18 ноября 2009

Вы не можете сделать что-то лучше, но у вашего JOIN не будет никакого фактического "JOIN" (никакой корреляции между T1.SOMECOL и T2.SOMECOL не будет, кроме того, что оба они имеют NULL значение для этого столбца). По сути это означает, что вы не сможете использовать JOIN для значений NULL, чтобы увидеть, соответствуют ли строки.

NULL никогда не равно другому NULL. Как может что-то неизвестного значения быть равно чему-то неизвестного значения?

1 голос
/ 18 ноября 2009

Вы действительно хотите иметь возможность объединять таблицы, если значение равно нулю? Разве вы не можете просто исключить возможные нулевые значения в предикате соединения? Мне трудно понять, что строки в двух таблицах могут быть связаны нулевым значением. Если у вас есть 100 нулей в table1.col_a и 100 нулей в table2.col_b, вы получите 10000 строк, возвращаемых только для строк с нулем. Звучит неправильно.

Однако вы сказали, что вам это нужно. Могу ли я предложить объединить пустой столбец в меньшую строку, так как сравнения символов относительно дороги. Еще лучше объединить нули в целое число, если данные в столбцах будут текстовыми. Тогда у вас будут очень быстрые «сравнения», и вы вряд ли столкнетесь с существующими данными.

0 голосов
/ 06 ноября 2017

Я думаю, что вы все еще можете использовать nvl () для соединения:

SELECT *
FROM T1
JOIN T2 ON NVL(T2.COL1,-1)=NVL(T1.COL1,-1);

Но вам нужно будет добавить индексы на основе функций для столбцов col1

CREATE INDEX IND_1 ON T1 (NVL(COL1,-1));
CREATE INDEX IND_2 ON T2 (NVL(COL1,-1));

Индексы должны значительно улучшить скорость объединения на NVL (..).

0 голосов
/ 26 августа 2016

Вы можете попробовать использовать с запросом ниже.

SELECT *
FROM TABLEA TA
JOIN TABLEB TB ON NVL(TA.COL1,0)=NVL(TB.COL2,0);
0 голосов
/ 18 марта 2016

Почему бы не что-то подобное:

SELECT * FROM T1 ПРИСОЕДИНЯЙТЕСЬ К T2 ON nvl (T1.SOMECOL, 'null') = NVL (T2.SOMECOL, 'нулевой')

Я не знаю, почему вы используете UUID. Вы можете использовать любую строку, отсутствующую в столбцах, например строку «ноль», например, для уменьшения занимаемой памяти. И решение, использующее nvl, намного быстрее, чем решение, использующее, например, or ... is null, предложенное Эриком Петроэле.

0 голосов
/ 27 января 2012

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

SELECT T1.COL1 FROM
(
   (SELECT (CASE WHEN COL1 IS NULL THEN 'X' ELSE COL1 END) AS COL1 FROM TABLE1) T1
   JOIN
   (SELECT (CASE WHEN COL1 IS NULL THEN 'X' ELSE COL1 END) AS COL1 FROM TABLE2) T2
)
ON T1.COL1=T2.COL1
...