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

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

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

кажется, что NVL может быть просто" базовым случаем " версии coalesce.

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

8 ответов


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

NVL is Oracle конкретные, он был введен в 80раньше не было никаких стандартов.

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

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

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

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

это работает почти 0.5 секунд, так как это порождает SYS_GUID()'s, несмотря на 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 ' s не генерируются, и запрос является мгновенным.


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

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

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

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

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


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

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.


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

отличия:

  1. NVL принимает только 2 аргумента, тогда как COALESCE может принимать несколько доводы
  2. NVL оценивает оба аргумента и сначала объединяет остановки возникновение ненулевого значения.
  3. NVL выполняет неявное преобразование типа данных на основе первого аргумента отдается ему. СПЛОТИТЬСЯ ожидает, что все аргументы должны быть одного типа.
  4. COALESCE дает проблемы в запросах, которые используют предложения объединения. Образец ниже
  5. COALESCE является стандартом ANSI, где as NVL является специфичным для Oracle.

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

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

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

пример для UNION use-case

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


еще одно доказательство того, что coalesce() не останавливает оценку с первым ненулевым значением:

SELECT COALESCE(1, my_sequence.nextval) AS answer FROM dual;

запустите это, затем проверьте my_sequence.currval;


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

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

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


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

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

Это неправильно, см. ниже. Аргументы могут быть разных типов данных, то есть также документирована: если все вхождения expr являются числовым типом данных или любым ненумерическим типом данных, который может быть неявно преобразован в числовой тип данных, то база данных Oracle определяет аргумент с наивысшим числовым приоритетом, неявно преобразует остальные аргументы для этого типа данных, и возвращает тип данных.. На самом деле это даже противоречит распространенному выражению "объединение останавливается при первом появлении ненулевого значения", иначе тестовый случай № 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!

NVL: замените null значением.

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

таблица: 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 мы можем достичь всех трех правил.