Получить ноль == ноль в SQL - PullRequest
44 голосов
/ 10 октября 2008

Я хочу найти таблицу базы данных по пустому столбцу. Иногда значение, которое я ищу, само по себе равно NULL. Так как Null равен нулю, даже NULL, говоря

where MYCOLUMN=SEARCHVALUE 

потерпит неудачу. Сейчас я должен прибегнуть к

where ((MYCOLUMN=SEARCHVALUE) OR (MYCOLUMN is NULL and SEARCHVALUE is NULL))

Есть ли более простой способ сказать это?

(я использую Oracle, если это имеет значение)

Ответы [ 11 ]

67 голосов
/ 10 октября 2008

Вы можете делать вещи IsNull или NVL, но это только заставит движок делать больше работы. Вы будете вызывать функции для преобразования столбцов, которые затем должны сравнивать результаты.

Используйте то, что у вас есть

where ((MYCOLUMN=SEARCHVALUE) OR (MYCOLUMN is NULL and SEARCHVALUE is NULL))
36 голосов
/ 10 октября 2008

@ Энди Лестер утверждает, что оригинальная форма запроса более эффективна, чем использование 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% дороже. Таким образом, даже если один фрагмент кода может выглядеть не так аккуратно или элегантно, как другой, он может быть значительно более эффективным. Я запускал эту процедуру несколько раз, и результаты были почти одинаковыми каждый раз. Слава Энди ...

13 голосов
/ 14 марта 2011

В Эксперт Oracle Database Architecture Я видел:

WHERE DECODE(MYCOLUMN, SEARCHVALUE, 1) = 1
12 голосов
/ 10 октября 2008

Не знаю, проще ли, но я иногда использовал

<code>
WHERE ISNULL(MyColumn, -1) = ISNULL(SearchValue, -1)

Замена "-1" некоторым значением, которое является допустимым для типа столбца, но которое вряд ли будет фактически найдено в данных.

ПРИМЕЧАНИЕ. Я использую MS SQL, а не Oracle, поэтому не уверен, что «ISNULL» действителен.

7 голосов
/ 10 октября 2008

Используйте NVL, чтобы заменить ноль на какое-то фиктивное значение с обеих сторон, как в:

WHERE NVL(MYCOLUMN,0) = NVL(SEARCHVALUE,0)
5 голосов
/ 10 октября 2008

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

Псевдокод следует.

if (SEARCHVALUE IS NULL) {
    condition = 'MYCOLUMN IS NULL'
} else {
    condition = 'MYCOLUMN=SEARCHVALUE'
}
runQuery(query,condition)
2 голосов
/ 09 ноября 2008

Если возможно внеполосное значение:

where coalesce(mycolumn, 'out-of-band') 
    = coalesce(searchvalue, 'out-of-band')
1 голос
/ 09 декабря 2008

Это также может сделать работу в 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
1 голос
/ 10 октября 2008

Попробуйте

WHERE NVL(mycolumn,'NULL') = NVL(searchvalue,'NULL')
0 голосов
/ 06 июня 2013

В такой ситуации мы часто сталкиваемся с нашими функциями 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)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...