Я хочу выполнить поиск в таблице базы данных в столбце, допускающем значение NULL. Иногда значение, которое я ищу, само по себе NULL. Поскольку Null ничего не значит, даже NULL, говоря
where MYCOLUMN=SEARCHVALUE
не удастся. Прямо сейчас я должен прибегнуть к
where ((MYCOLUMN=SEARCHVALUE) OR (MYCOLUMN is NULL and SEARCHVALUE is NULL))
Есть ли более простой способ сказать это?
(Я использую Oracle, если это важно)


Пытаться
WHERE NVL(mycolumn,'NULL') = NVL(searchvalue,'NULL')
что произойдет, если mycolumn имеет значение NULL, а значение searchvalue равно NULL; или наоборот?
Решение этого комментария оставлено читателю в качестве упражнения ;-)
Я не получил голосов против. Ответ неверный? Если да, то любезно изложите свои возражения. Возможно, я мог бы скорректировать свой ответ, чтобы обратиться к ним. Что касается первоначального комментария, то это была концептуальная иллюстрация. Я думал, что было довольно ясно, что нужно использовать значение, которое не будет отображаться в ваших данных ...
Используйте NVL для замены null некоторым фиктивным значением с обеих сторон, как в:
WHERE NVL(MYCOLUMN,0) = NVL(SEARCHVALUE,0)
что произойдет, если значение mycolumn равно нулю, а значение поиска равно 0; или наоборот?
Если 0 - это что-то, что может встречаться в ваших данных или в поисковом значении, вам нужно использовать другое фиктивное значение. Если вы не можете найти подходящее фиктивное значение, вы не можете использовать этот подход.
Вам вообще не нужно использовать этот подход. Нет причин сравнивать с фиктивными значениями, когда вы можете напрямую сравнивать с фактическим NULL.
Не знаю, проще ли, но иногда использовал
WHERE ISNULL(MyColumn, -1) = ISNULL(SearchValue, -1)
Замена «-1» некоторым значением, допустимым для данного типа столбца, но которое вряд ли может быть обнаружено в данных.
ПРИМЕЧАНИЕ. Я использую MS SQL, а не Oracle, поэтому не уверен, действительно ли ISNULL.
Эквивалентная функция Oracle - NVL. Тот же синтаксис.
Кстати, вы можете сделать то же самое с Coalesce (MyColumn, -1) = Coalesce (SearchValue, -1).
Меня не совсем устраивают предлагаемые критерии выбора значения замены NULL. Достаточно ли критерия «вряд ли будет найдено»? Что происходит, когда по обе стороны от теста на равенство появляется «маловероятное» значение. Приемлемо ли теперь сопоставить NULL с «маловероятным» значением?
Это развалится, если любое из значений может на законных основаниях быть -1, и тогда вы будете выдергивать волосы, чтобы найти ошибку.
Вы можете использовать IsNull или NVL, но это просто заставит движок работать больше. Вы будете вызывать функции для преобразования столбцов, которые затем должны сравнивать результаты.
Используйте то, что у вас есть
where ((MYCOLUMN=SEARCHVALUE) OR (MYCOLUMN is NULL and SEARCHVALUE is NULL))
Я думаю, что у вас все в порядке. Вы могли бы использовать:
where NVL(MYCOLUMN, '') = NVL(SEARCHVALUE, '')
Это не сработает, потому что пустая строка эквивалентна NULL в Oracle, что возвращает нас к тому же сравнению NULL == NULL.
Черт, теперь я вспомнил эту маленькую глупость! Я думаю, что оставлю этот ответ как напоминание всем остальным
Другой альтернативой, которая, вероятно, является оптимальной с точки зрения выполняемого запроса, и будет полезно только в том случае, если вы выполняете какую-то генерацию запросов является создание точного запроса, который вам нужен, на основе значения поиска.
Далее следует псевдокод.
if (SEARCHVALUE IS NULL) {
condition = 'MYCOLUMN IS NULL'
} else {
condition = 'MYCOLUMN=SEARCHVALUE'
}
runQuery(query,condition)
за исключением того, что вы должны использовать переменную привязки!
Я не думаю, что вы могли бы использовать здесь переменную привязки, не нарушая свою цель
@Andy Lester утверждает, что исходная форма запроса более эффективна, чем использование NVL. Я решил проверить это утверждение:
SQL> DECLARE
2 CURSOR B IS
3 SELECT batch_id, equipment_id
4 FROM batch;
5 v_t1 NUMBER;
6 v_t2 NUMBER;
7 v_c1 NUMBER;
8 v_c2 NUMBER;
9 v_b INTEGER;
10 BEGIN
11 -- Form 1 of the where clause
12 v_t1 := dbms_utility.get_time;
13 v_c1 := dbms_utility.get_cpu_time;
14 FOR R IN B LOOP
15 SELECT COUNT(*)
16 INTO v_b
17 FROM batch
18 WHERE equipment_id = R.equipment_id OR (equipment_id IS NULL AND R.equipment_id IS NULL);
19 END LOOP;
20 v_t2 := dbms_utility.get_time;
21 v_c2 := dbms_utility.get_cpu_time;
22 dbms_output.put_line('For clause: WHERE equipment_id = R.equipment_id OR (equipment_id IS NULL AND R.equipment_id IS NULL)');
23 dbms_output.put_line('CPU seconds used: '||(v_c2 - v_c1)/100);
24 dbms_output.put_line('Elapsed time: '||(v_t2 - v_t1)/100);
25
26 -- Form 2 of the where clause
27 v_t1 := dbms_utility.get_time;
28 v_c1 := dbms_utility.get_cpu_time;
29 FOR R IN B LOOP
30 SELECT COUNT(*)
31 INTO v_b
32 FROM batch
33 WHERE NVL(equipment_id,'xxxx') = NVL(R.equipment_id,'xxxx');
34 END LOOP;
35 v_t2 := dbms_utility.get_time;
36 v_c2 := dbms_utility.get_cpu_time;
37 dbms_output.put_line('For clause: WHERE NVL(equipment_id,''xxxx'') = NVL(R.equipment_id,''xxxx'')');
38 dbms_output.put_line('CPU seconds used: '||(v_c2 - v_c1)/100);
39 dbms_output.put_line('Elapsed time: '||(v_t2 - v_t1)/100);
40 END;
41 /
For clause: WHERE equipment_id = R.equipment_id OR (equipment_id IS NULL AND R.equipment_id IS NULL)
CPU seconds used: 84.69
Elapsed time: 84.8
For clause: WHERE NVL(equipment_id,'xxxx') = NVL(R.equipment_id,'xxxx')
CPU seconds used: 124
Elapsed time: 124.01
PL/SQL procedure successfully completed
SQL> select count(*) from batch;
COUNT(*)
----------
20903
SQL>
Я был немного удивлен, узнав, насколько прав Энди. Решение NVL стоит почти на 50% дороже. Таким образом, даже если один фрагмент кода может выглядеть не так аккуратно или элегантно, как другой, он может быть значительно более эффективным. Я запускал эту процедуру несколько раз, и каждый раз результаты были почти одинаковыми. Престижность Энди ...
Отлично сделано ... Возможно, мне придется позаимствовать кое-что из этого, только для тестовой среды.
Также учтите, что выполнение этих преобразований означает, что движок не может использовать индекс, если он есть в столбце. Например, вот так: где? = foo + 1 не может использовать индекс для foo, но где? - 1 = foo может использовать индекс. Вы часто будете сталкиваться с этим с указателями даты.
Вот тест Тома: asktom.oracle.com/pls/asktom/…
Если возможно внеполосное значение:
where coalesce(mycolumn, 'out-of-band')
= coalesce(searchvalue, 'out-of-band')
Это также может сделать работу в Oracle.
WHERE MYCOLUMN || 'X' = SEARCHVALUE || 'X'
В некоторых ситуациях он превосходит тест IS NULL с оператором OR.
Я также был удивлен, что DECODE позволяет вам проверять NULL на NULL.
WITH
TEST AS
(
SELECT NULL A FROM DUAL
)
SELECT DECODE (A, NULL, 'NULL IS EQUAL', 'NULL IS NOT EQUAL')
FROM TEST
В стандартном SQL конкатенация с NULL дает NULL, поэтому вы получите NULL = NULL, что неверно. Позволяет ли Oracle этому работать?
Я успешно использовал это в оракуле.
Я бы хотел изменить свой голос против (извините за поспешность) на голос за, чего я не могу сделать, пока вы не отредактируете свой ответ. Было бы неплохо добавить примечание о типах данных MYCOLUMN и SEARCHVALUE - работает ли это для всех символьных типов данных, всех числовых? любой другой?
@Джек. Я не проводил формального расследования всех типов данных. Я использовал его с VARCHAR2, поэтому в худшем случае вы можете преобразовать его в VARCHAR2.
В Экспертная архитектура базы данных Oracle я видел:
WHERE DECODE(MYCOLUMN, SEARCHVALUE, 1) = 1
Это ситуация, в которой мы часто сталкиваемся с нашими функциями Oracle, которые управляют отчетами. Мы хотим разрешить пользователям вводить значение для ограничения результатов или оставлять поле пустым, чтобы возвращать все записи. Это то, что я использовал, и это хорошо сработало для нас.
WHERE rte_pending.ltr_rte_id = prte_id
OR ((rte_pending.ltr_rte_id IS NULL OR rte_pending.ltr_rte_id IS NOT NULL)
AND prte_id IS NULL)
Совершенно очевидно, что
NVL«замедлит работу», особенно для индексированных столбцов.