Различия Oracle между NVL и Coalesce

Существуют ли очевидные различия между NVL и Coalesce в Oracle?

Очевидные различия заключаются в том, что coalesce вернет первый ненулевой элемент в свой список параметров, тогда как nvl принимает только два параметра и возвращает первый, если он не является нулевым, иначе он возвращает второй.

Похоже, что NVL может быть просто "базовым" вариантом объединения.

Я что-то пропустил?

Ответ 1

COALESCE - более современная функция, которая является частью стандарта ANSI-92.

NVL имеет значение Oracle, он был введен в 80 до того, как были какие-либо стандарты.

В случае двух значений они являются синонимами.

Однако они реализованы по-разному.

NVL всегда оценивает оба аргумента, а COALESCE останавливает оценку всякий раз, когда находит первый не NULL:

SELECT  SUM(val)
FROM    (
        SELECT  NVL(1, LENGTH(RAWTOHEX(SYS_GUID()))) AS val
        FROM    dual
        CONNECT BY
                level <= 10000
        )

Это выполняется почти в течение 0.5 секунд, так как он генерирует SYS_GUID(), несмотря на то, что 1 не является NULL.

SELECT  SUM(val)
FROM    (
        SELECT  COALESCE(1, LENGTH(RAWTOHEX(SYS_GUID()))) AS val
        FROM    dual
        CONNECT BY
                level <= 10000
        )

Это понимает, что 1 не является NULL и не оценивает второй аргумент.

SYS_GUID не генерируются и запрос выполняется мгновенно.

Ответ 2

NVL выполнит неявное преобразование в тип данных первого параметра, поэтому следующее не вызывает ошибки

select nvl('a',sysdate) from dual;

COALESCE ожидает последовательных типов данных.

select coalesce('a',sysdate) from dual;

выдает "несогласованную ошибку типа данных"

Ответ 3

Существует также разница в обработке плана.

Oracle может сформировать оптимизированный план с конкатенацией фильтров ветвей, когда поиск содержит сравнение результата nvl с индексированным столбцом.

create table tt(a, b) as
select level, mod(level,10)
from dual
connect by level<=1e4;

alter table tt add constraint ix_tt_a primary key(a);
create index ix_tt_b on tt(b);

explain plan for
select * from tt
where a=nvl(:1,a)
  and b=:2;

explain plan for
select * from tt
where a=coalesce(:1,a)
  and b=:2;

NVL:

-----------------------------------------------------------------------------------------
| Id  | Operation                     | Name    | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |         |     2 |    52 |     2   (0)| 00:00:01 |
|   1 |  CONCATENATION                |         |       |       |            |          |
|*  2 |   FILTER                      |         |       |       |            |          |
|*  3 |    TABLE ACCESS BY INDEX ROWID| TT      |     1 |    26 |     1   (0)| 00:00:01 |
|*  4 |     INDEX RANGE SCAN          | IX_TT_B |     7 |       |     1   (0)| 00:00:01 |
|*  5 |   FILTER                      |         |       |       |            |          |
|*  6 |    TABLE ACCESS BY INDEX ROWID| TT      |     1 |    26 |     1   (0)| 00:00:01 |
|*  7 |     INDEX UNIQUE SCAN         | IX_TT_A |     1 |       |     1   (0)| 00:00:01 |
-----------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   2 - filter(:1 IS NULL)
   3 - filter("A" IS NOT NULL)
   4 - access("B"=TO_NUMBER(:2))
   5 - filter(:1 IS NOT NULL)
   6 - filter("B"=TO_NUMBER(:2))
   7 - access("A"=:1)

сливаются:

---------------------------------------------------------------------------------------
| Id  | Operation                   | Name    | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |         |     1 |    26 |     1   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS BY INDEX ROWID| TT      |     1 |    26 |     1   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN          | IX_TT_B |    40 |       |     1   (0)| 00:00:01 |
---------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter("A"=COALESCE(:1,"A"))
   2 - access("B"=TO_NUMBER(:2))

Кредиты идут в http://www.xt-r.com/2012/03/nvl-coalesce-concatenation.html.

Ответ 4

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

Различия заключаются в следующем:

  • NVL принимает только 2 аргумента, тогда как COALESCE может принимать несколько Аргументы
  • NVL оценивает как аргументы, так и COALESCE останавливается сначала появление значения, отличного от Null.
  • NVL выполняет неявное преобразование типа данных, основанное на первом аргументе данный ему. COALESCE ожидает, что все аргументы будут иметь одинаковый тип данных.
  • COALESCE дает проблемы в запросах, которые используют предложения UNION. пример ниже
  • COALESCE - стандарт ANSI, где NVL является специфичным для Oracle.

Примеры для третьего случая. Другие случаи просты.

select nvl('abc',10) from dual; будет работать, поскольку NVL выполнит неявное преобразование числовых 10 в строку.

select coalesce('abc',10) from dual; завершится с ошибкой - непоследовательные типы данных: ожидаемый CHAR получил NUMBER

Пример использования прецедента UNION

SELECT COALESCE(a, sysdate) 
from (select null as a from dual 
      union 
      select null as a from dual
      );

не работает с ORA-00932: inconsistent datatypes: expected CHAR got DATE

SELECT NVL(a, sysdate) 
from (select null as a from dual 
      union 
      select null as a from dual
      ) ;

преуспевает.

Дополнительная информация: http://www.plsqlinformation.com/2016/04/difference-between-nvl-and-coalesce-in-oracle.html

Ответ 5

Хотя этот факт очевиден и даже упомянут в вопросе, поставленном Томом, который задал этот вопрос. Но давайте снова встанем.

NVL может иметь только 2 аргумента. Coalesce может иметь более 2.

select nvl('','',1) from dual;//Результат: ORA-00909: неверное количество аргументов
select coalesce('','','1') from dual;//Выход: возвращает 1

Ответ 6

На самом деле я не могу согласиться с каждым выражением.

"COALESCE ожидает, что все аргументы будут иметь один и тот же тип данных."

Это неправильно, см. ниже. Аргументами могут быть разные типы данных, а также documented: Если все вхождения expr являются числовыми типами данных или любым нечетным типом данных, которые могут быть неявно преобразованный в числовой тип данных, тогда Oracle Database определяет аргумент с наивысшим числовым приоритетом, неявно преобразует остальные аргументы в этот тип данных и возвращает этот тип данных. Фактически это даже противоречит общему выражению "COALESCE останавливается сначала появление значения, отличного от Null", в противном случае тестовый пример № 4 не должен приводить к ошибке.

Также в соответствии с тестовым примером № 5 COALESCE выполняется неявное преобразование аргументов.

DECLARE
    int_val INTEGER := 1;
    string_val VARCHAR2(10) := 'foo';
BEGIN

    BEGIN
    DBMS_OUTPUT.PUT_LINE( '1. NVL(int_val,string_val) -> '|| NVL(int_val,string_val) );
    EXCEPTION WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('1. NVL(int_val,string_val) -> '||SQLERRM ); 
    END;

    BEGIN
    DBMS_OUTPUT.PUT_LINE( '2. NVL(string_val, int_val) -> '|| NVL(string_val, int_val) );
    EXCEPTION WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('2. NVL(string_val, int_val) -> '||SQLERRM ); 
    END;

    BEGIN
    DBMS_OUTPUT.PUT_LINE( '3. COALESCE(int_val,string_val) -> '|| COALESCE(int_val,string_val) );
    EXCEPTION WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('3. COALESCE(int_val,string_val) -> '||SQLERRM ); 
    END;

    BEGIN
    DBMS_OUTPUT.PUT_LINE( '4. COALESCE(string_val, int_val) -> '|| COALESCE(string_val, int_val) );
    EXCEPTION WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('4. COALESCE(string_val, int_val) -> '||SQLERRM ); 
    END;

    DBMS_OUTPUT.PUT_LINE( '5. COALESCE(SYSDATE,SYSTIMESTAMP) -> '|| COALESCE(SYSDATE,SYSTIMESTAMP) );

END;
Output:

1. NVL(int_val,string_val) -> ORA-06502: PL/SQL: numeric or value error: character to number conversion error
2. NVL(string_val, int_val) -> foo
3. COALESCE(int_val,string_val) -> 1
4. COALESCE(string_val, int_val) -> ORA-06502: PL/SQL: numeric or value error: character to number conversion error
5. COALESCE(SYSDATE,SYSTIMESTAMP) -> 2016-11-30 09:55:55.000000 +1:0 --> This is a TIMESTAMP value, not a DATE value!

Ответ 7

NVL: Замените нуль на значение.

COALESCE: Возвращает первое ненулевое выражение из списка выражений.

Таблица: PRICE_LIST

+----------------+-----------+
| Purchase_Price | Min_Price |
+----------------+-----------+
| 10             | null      |
| 20             |           |
| 50             | 30        |
| 100            | 80        |
| null           | null      |
+----------------+-----------+   

Ниже приведен пример

[1] Установите цену продажи с добавлением 10% прибыли для всех продуктов.
[2] Если цена покупки не указана, тогда цена продажи является минимальной ценой. Для продажи.
[3] Если минимальная цена также не установлена, тогда установить цену продажи по умолчанию "50".

SELECT
     Purchase_Price,
     Min_Price,
     NVL(Purchase_Price + (Purchase_Price * 0.10), Min_Price)    AS NVL_Sales_Price,
COALESCE(Purchase_Price + (Purchase_Price * 0.10), Min_Price,50) AS Coalesce_Sales_Price
FROM 
Price_List

Объясните с практическим примером реальной жизни.

+----------------+-----------+-----------------+----------------------+
| Purchase_Price | Min_Price | NVL_Sales_Price | Coalesce_Sales_Price |
+----------------+-----------+-----------------+----------------------+
| 10             | null      | 11              |                   11 |
| null           | 20        | 20              |                   20 |
| 50             | 30        | 55              |                   55 |
| 100            | 80        | 110             |                  110 |
| null           | null      | null            |                   50 |
+----------------+-----------+-----------------+----------------------+

Вы можете видеть, что с NVL мы можем достичь правил [1], [2]
Но с COALSECE мы можем достичь всех трех правил.