Oracle nvl в где пункт, показывающий странные результаты? - PullRequest
2 голосов
/ 03 марта 2011

У меня есть веб-форма, которая позволяет пользователям искать и редактировать записи из таблицы Oracle на основе параметров, передаваемых в процесс. Вот мои данные:

CAE_SEC_ID  SEC_CODE  APPR_STATUS
1           ABC1      100
2           ABC2      100
3           ABC3      101
4           (null)    101
5           (null)    102
6           ABC4      103

А вот предложение where:

select foo 
  from bar 
 where CAE_SEC_ID = NVL(p_cae_sec_id,CAE_SEC_ID)
   and Upper(SEC_CODE) like '%' || Upper(NVL(p_sec_code,SEC_CODE)) || '%'
   and APPR_STATUS = NVL(p_appr_status, APPR_STATUS)

Использование nvl для параметров должно возвращать только совпадающие записи, если какой-либо из параметров имеет значения, и все записи, если ни один из параметров не имеет значений. Все довольно стандартно или так я думал. Однако когда я выполняю поиск без каких-либо значений параметров, запрос не возвращает записи с нулевым SEC_CODE, то есть возвращаются только записи 1, 2, 3 и 6. Разве вышеприведенное предложение не должно включать записи с нулевыми значениями SEC_CODE?

Ответы [ 4 ]

7 голосов
/ 03 марта 2011

Проблема в том, что значение SEC_CODE в таблице равно NULL.Это означает, что UPPER(sec_code) равен NULL, а ваш второй предикат упрощается до

and NULL LIKE '%%'

Так же, как NULL не равен ничему и не равен ничему, он не похож ни на что.Скорее всего, вам нужно что-то вроде

and (Upper(SEC_CODE) like '%' || Upper(NVL(p_sec_code,SEC_CODE)) || '%' or
     (sec_code is null and p_sec_code is null))

, которое будет возвращать каждую строку, если P_SEC_CODE равно NULL, но все равно применяет фильтр, если P_SEC_CODE не равно NULL.

4 голосов
/ 03 марта 2011

Нет, не должно.

Значение SEC_CODE в базе данных равно нулю, поэтому значение UPPER (SEC_CODE) равно нулю, поэтому при совпадении типа LIKE или при любом другом сравнении, кроме IS NULL, произойдет сбой.Технически это НЕИЗВЕСТНО, а не ЛОЖЬ, но этого недостаточно для прохождения теста.

3 голосов
/ 03 марта 2011

Выражение NULL = NULL оценивается как NULL, что не равно true, поэтому эти строки возвращаться не будут.Если я правильно понимаю требование, вам нужно фильтровать, только если параметр отличается от нуля, поэтому я бы переписал запрос следующим образом, чтобы сделать фильтр более явным:

select foo from bar 
where (p_cae_sec_id is null or CAE_SEC_ID = p_cae_sec_id)
and (p_sec_code is null or Upper(SEC_CODE) like '%' || Upper(p_sec_code) || '%')
and (p_appr_status is null or APPR_STATUS = p_appr_status)

Установка для параметра p_sec_code значения nullтеперь будет возвращать все строки, игнорируя значение в столбце SEC_CODE.

0 голосов
/ 03 марта 2011

Мы также можем написать запрос Йорна как

select foo from bar 
where (CASE WHEN p_cae_sec_id is null THEN 'Y'
            WHEN CAE_SEC_ID = p_cae_sec_id THEN 'Y'
                    ELSE 'N'
                    END)='Y'
and   (CASE WHEN p_sec_code is null THEN 'Y'
            WHEN Upper(SEC_CODE) like '%' || Upper(p_sec_code) || '%' THEN 'Y'
                    ELSE 'N'
                    END)='Y'
and (CASE WHEN p_appr_status is null THEN 'Y'
                    WHEN APPR_STATUS = p_appr_status THEN 'Y'
                ELSE 'N'
                END)='Y'

, чтобы сделать его конкретным и повысить производительность.

...