Відмінності Oracle між NVL та Coalesce


208

Чи є очевидні відмінності між NVL та Coalesce в Oracle?

Очевидні відмінності полягають у тому, що coalesce поверне перший ненульовий елемент у своєму списку параметрів, тоді як nvl бере лише два параметри та повертає перший, якщо він не є нульовим, інакше він повертає другий.

Схоже, NVL може бути просто спільною версією "Base Case".

Я щось пропускаю?


Відповіді:


312

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

NVLє 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(), незважаючи на 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не створюються, і запит є миттєвим.


11
Вони не є точно синонімами ... Принаймні, ви можете знайти різницю в тому, що NVL робить неявне кастинг типів даних, якщо дані значення мають різні типи. Так, наприклад, я отримував помилку, використовуючи COALESCE, передаючи йому два значення NULL (одне явно встановлене, а інше, взятте зі стовпця в базі даних типу NUMBER), які просто зникають, змінюючи функцію на NVL.
DanielM

170

NVL здійснить неявне перетворення у тип даних першого параметра, тому наступне не помиляється

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

COALESCE очікує послідовних типів даних.

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

видалить "непослідовну помилку типу даних"


22

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

Відмінності:

  1. NVL приймає лише 2 аргументи, тоді як COALESCE може приймати кілька аргументів
  2. NVL оцінює і аргументи, і COALESCE зупиняється при першому появі ненульового значення.
  3. NVL робить неявне перетворення типу даних на основі першого аргументу, який йому дано. COALESCE розраховує, що всі аргументи мають однаковий тип даних.
  4. COALESCE задає питання у запитах, які використовують пункти UNION. Приклад нижче
  5. 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-bet between-nvl-and-coalesce-in-oracle.html


Я не думаю, що існує певна проблема із "об'єднанням", тому здається, що Oracle бажає ввести під нульовим запитом каре "null" до знаку char, і тоді у вас є та сама проблема, що вказана у вашому пункті 3 (змішані дані типи). Якщо ви зміните його на TO_DATE (NULL), ви, ймовірно, не отримаєте помилку (я не можу відтворити помилку у версії Oracle, яку я використовую). В іншому випадку я погоджуюсь і ціную вашу відповідь. :-)
плескіт

17

Існує також різниця в плані обробки.

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 .


6

Ще один доказ того, що coalesce () не зупиняє оцінку з першим ненульовим значенням:

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

Виконати це, а потім перевірити my_sequence.currval;


5

Насправді я не можу погодитися на кожне твердження.

"COALESCE очікує, що всі аргументи мають однаковий тип даних."

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

1
Re: Тест 4 суперечить "COALESCE зупиняє оцінку при першому ненульовому значенні" . Я не погоджуюсь. Тест 4 показує, що компілятор перевіряє відповідність типу даних COALESCE. Зупинення першого ненульового значення - це проблема часу виконання, а не проблема часу компіляції. Під час компіляції компілятор не знає, що третє значення (скажіть) буде недійсним; він наполягає на тому, щоб четвертий аргумент також був правильним типом даних, навіть якщо це четверте значення ніколи насправді не буде оцінено.
mathguy

3

Хоча це очевидно і навіть згадується у спосіб, поставлений Томом, який задав це питання. Але давайте миримося знову.

NVL може мати лише 2 аргументи. Злиття може мати більше 2.

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


3

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 ми можемо досягти всіх трьох правил.


про що ти кажеш NVL(Purchase_Price + (Purchase_Price * 0.10), nvl(Min_Price,50)) . Або про: nvl(NVL(Purchase_Price + (Purchase_Price * 0.10), Min_Price) ,50) :)
Флорін Гіта

що швидше, ефективніше, що слід використовувати? розглядаючи тисячі записів для завантаження?
rickyProgrammer
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.