Oracle Differenze tra NVL e Coalesce


208

Ci sono differenze non ovvie tra NVL e Coalesce in Oracle?

Le differenze ovvie sono che la coesione restituirà il primo elemento non nullo nella sua lista di parametri mentre nvl accetta solo due parametri e restituisce il primo se non è nullo, altrimenti restituisce il secondo.

Sembra che NVL potrebbe essere solo una versione di "caso base" di coalescenza.

Mi sto perdendo qualcosa?


Risposte:


312

COALESCEè una funzione più moderna che fa parte dello ANSI-92standard.

NVLè Oraclespecifico, è stato introdotto 80prima che esistessero degli standard.

Nel caso di due valori, sono sinonimi.

Tuttavia, sono implementati in modo diverso.

NVLvaluta sempre entrambi gli argomenti, mentre di COALESCEsolito interrompe la valutazione ogni volta che trova il primo non- NULL(ci sono alcune eccezioni, come la sequenza NEXTVAL):

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

Funziona per quasi 0.5secondi, dal momento che genera SYS_GUID(), nonostante 1non sia un NULL.

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

Questo capisce che 1non è un NULLe non valuta il secondo argomento.

SYS_GUIDNon vengono generati e la query è istantanea.


11
Non sono esattamente sinonimi ... Almeno puoi trovare una differenza nel fatto che NVL esegue un cast implicito di tipi di dati se i valori indicati sono di tipi diversi. Quindi, ad esempio, stavo ottenendo un errore usando COALESCE passando due valori NULL (uno impostato esplicitamente e l'altro preso da una colonna nel database, di tipo NUMBER), che scompaiono semplicemente cambiando la funzione in NVL.
DanielM

170

NVL eseguirà una conversione implicita nel tipo di dati del primo parametro, quindi non si verifica l'errore

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

COALESCE prevede tipi di dati coerenti.

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

genererà un "errore di tipo di dati incoerente"


22

NVL e COALESCE vengono utilizzati per ottenere la stessa funzionalità di fornire un valore predefinito nel caso in cui la colonna restituisca un valore NULL.

Le differenze sono:

  1. NVL accetta solo 2 argomenti mentre COALESCE può accettare più argomenti
  2. NVL valuta sia gli argomenti che COALESCE si interrompe alla prima occorrenza di un valore non Null.
  3. NVL esegue una conversione di tipo di dati implicita in base al primo argomento fornito. COALESCE si aspetta che tutti gli argomenti siano dello stesso tipo di dati.
  4. COALESCE presenta problemi nelle query che utilizzano clausole UNION. Esempio sotto
  5. COALESCE è uno standard ANSI in cui NVL è specifico per Oracle.

Esempi per il terzo caso. Altri casi sono semplici.

select nvl('abc',10) from dual; funzionerebbe poiché NVL eseguirà una conversione implicita di 10 numerici in stringa.

select coalesce('abc',10) from dual; fallirà con Errore - tipi di dati incoerenti: il CHAR previsto ha ottenuto NUMBER

Esempio per il caso d'uso UNION

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

fallisce con 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
      ) ;

riesce.

Ulteriori informazioni: http://www.plsqlinformation.com/2016/04/difference-between-nvl-and-coalesce-in-oracle.html


Non credo che ci sia un problema specifico con "unione" così tanto che Oracle vuole digitare cast null nella tua sottoquery su un carattere di default e quindi hai lo stesso problema elencato nel tuo articolo 3 (dati misti tipi). Se lo cambi in TO_DATE (NULL) probabilmente non otterrai l'errore (non riesco a riprodurre l'errore sulla versione di Oracle che sto usando). In caso contrario, sono d'accordo e apprezzo la tua risposta. :-)
splashout

17

C'è anche una differenza nella gestione dei piani.

Oracle è in grado di formare un piano ottimizzato con concatenazione di filtri di diramazione quando la ricerca contiene il confronto dei nvlrisultati con una colonna indicizzata.

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)

coalesce:

---------------------------------------------------------------------------------------
| 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))

I crediti vanno a http://www.xt-r.com/2012/03/nvl-coalesce-concatenation.html .


6

Un'altra prova che coalesce () non ferma la valutazione con il primo valore non nullo:

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

Esegui questo, quindi controlla my_sequence.currval;


5

In realtà non posso essere d'accordo con ogni affermazione.

"COALESCE si aspetta che tutti gli argomenti siano dello stesso tipo di dati."

Questo è sbagliato, vedi sotto. Gli argomenti possono essere diversi tipi di dati, anche questo è documentato : se tutte le occorrenze di expr sono di tipo numerico o qualsiasi tipo di dati non numerico che può essere implicitamente convertito in un tipo di dati numerico, Oracle Database determina l'argomento con la precedenza numerica più alta, implicitamente converte gli argomenti rimanenti in quel tipo di dati e restituisce quel tipo di dati.. In realtà questo è persino in contraddizione con l'espressione comune "COALESCE si interrompe alla prima occorrenza di un valore non Null", altrimenti il ​​caso di test n. 4 non dovrebbe generare un errore.

Anche secondo il caso di prova n. 5 si COALESCEottiene una conversione implicita di argomenti.

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
Ri: Test 4 contraddice "COALESCE interrompe la valutazione al primo valore non nullo" . Non sono d'accordo. Il test 4 mostra che il compilatore verifica la coerenza del tipo di dati con COALESCE. L'arresto al primo valore non nullo è un problema di runtime, non un problema di compilazione. Al momento della compilazione il compilatore non sa che il terzo valore (diciamo) sarà non nullo; insiste sul fatto che anche il quarto argomento sia del giusto tipo di dati, anche se quel quarto valore non verrà mai effettivamente valutato.
Mathguy,

3

Anche se questo è ovvio, e persino menzionato in un modo presentato da Tom che ha posto questa domanda. Ma rimettiamo in piedi.

NVL può avere solo 2 argomenti. Coalesce può avere più di 2.

select nvl('','',1) from dual;// Risultato:: ORA-00909numero di argomenti non valido
select coalesce('','','1') from dual; // Output: restituisce 1


3

NVL: sostituire il valore null con valore.

COALESCE: restituisce la prima espressione non nulla dall'elenco delle espressioni.

Tabella: PRICE_LIST

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

Di seguito è riportato l'esempio di

[1] Imposta il prezzo di vendita con l'aggiunta del profitto del 10% a tutti i prodotti.
[2] Se non è presente alcun prezzo di listino, il prezzo di vendita è il prezzo minimo. Per liquidazione.
[3] Se non esiste anche un prezzo minimo, imposta il prezzo di vendita come prezzo predefinito "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

Spiega con l'esempio pratico della vita reale.

+----------------+-----------+-----------------+----------------------+
| 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 |
+----------------+-----------+-----------------+----------------------+

Puoi vedere che con NVL possiamo raggiungere le regole [1], [2]
Ma con COALSECE possiamo raggiungere tutte e tre le regole.


quello che dici NVL(Purchase_Price + (Purchase_Price * 0.10), nvl(Min_Price,50)) . O circa: nvl(NVL(Purchase_Price + (Purchase_Price * 0.10), Min_Price) ,50) :)
Florin Ghita,

quale è più veloce, per quanto riguarda le prestazioni cosa dovrebbe essere usato? considerando migliaia di record da caricare?
rickyProgrammer,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.