2009-06-04 10 views
158

Esistono differenze non ovvie tra NVL e Coalesce in Oracle?Differenze Oracle tra NVL e Coalesce

Le ovvie differenze sono che fondono ritorna al primo elemento non nullo nella sua lista di parametri che nvl richiede 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 coalesce.

mi sto perdendo qualcosa?

+0

Maggiori informazioni qui: https://jonathanlewis.wordpress.com/2018/02/13/coalesce-v-nvl/ –

risposta

249

COALESCE è la funzione più moderno che è una parte di ANSI-92 standard.

NVL è Oracle specifico, è stato introdotto nel 80 's prima che ci fossero standard.

nel caso di due valori, sono sinonimi .

Tuttavia, vengono implementate in modo diverso.

NVL valuta sempre entrambi gli argomenti, mentre COALESCE di solito si ferma 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 
     ) 

Questo viene eseguito per quasi 0.5 secondi, dal momento che genera SYS_GUID()'s, nonostante 1 non 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 non è un 1NULL e non valutare il secondo argomento.

SYS_GUID 's non vengono generati e la query è immediata.

+2

Fantastico, grazie. Immagino che ci fosse una specie di trucco. –

+6

Non sono esattamente sinonimi ... Almeno puoi trovare una differenza nel fatto che NVL effettua un casting di tipi di dati impliciti se i valori dati sono di tipi diversi. Quindi, ad esempio, ricevevo un errore usando COALESCE passandogli due valori NULL (uno impostato esplicitamente e l'altro prelevato da una colonna nel database, di tipo NUMBER), che scompare semplicemente cambiando la funzione in NVL. – DanielM

152

NVL farà una conversione implicita per il tipo di dati del primo parametro, quindi il seguente errore non

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

COALESCE aspetta tipi di dati coerenti.

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

getterà un 'errore di tipo di dati incoerenti'

15

C'è anche differenza è nella gestione piano.

Oracle è in grado forma un piano ottimizzato concatenazione di filtri ramo quando ricerca contiene confronto di nvl risultato 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) 

fondono:

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

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

3

Anche se questo è ovvio, e nemmeno menzionato in un modo messo da Tom che ha chiesto questa domanda. Ma lasciamo riposare di nuovo.

NVL possono avere solo 2 argomenti. Fondersi può avere più di 2.

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

13

NVL e COALESCE sono utilizzati per ottenere le stesse funzionalità di fornire un valore di default nel caso in cui la colonna restituisca un valore NULL.

Le differenze sono:

  1. NVL accetta solo 2 argomenti mentre COALESCE possono richiedere più argomenti
  2. NVL valuta sia gli argomenti e COALESCE ferma al primo occorrenza di un valore non nullo.
  3. NVL esegue una conversione di tipo di dati implicito basato sul primo argomento dato ad essa. COALESCE si aspetta che tutti gli argomenti siano dello stesso tipo di dati.
  4. COALESCE fornisce problemi nelle query che utilizzano clausole UNION. Esempio sotto
  5. COALESCE è ANSI standard in cui come NVL è Oracle specifico.

esempi per il terzo caso. Altri casi sono semplici.

select nvl('abc',10) from dual; funzionerebbe come NVL farà una conversione implicita di numerico 10 a stringa.

select coalesce('abc',10) from dual; verrà effettuata con errore - incoerente tipi di dati: atteso CHAR ottenuto NUMERO

Esempio di UNION caso d'uso

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.

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

1

NVL: Sostituire il nulla con il valore.

COALESCE: Restituisce la prima espressione non nulla dalla lista di espressioni.

Tabella: price_list

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

Di seguito è riportato l'esempio di

[1] Set prezzo di vendita con l'aggiunta di 10% di profitto a tutti i prodotti.
[2] Se non esiste il prezzo di listino acquisti, il prezzo di vendita è il prezzo minimo. Per la liquidazione.
[3] Se non esiste anche un prezzo minimo, impostare 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 

spiegare con le esempio pratico di 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 | 
+----------------+-----------+-----------------+----------------------+ 

Si può vedere che con NVL possiamo raggiungere regole [1], [2]
Ma con COALSECE siamo in grado di raggiungere tutte le tre regole.

+0

cosa dici di 'NVL (Purchase_Price + (Purchase_Price * 0.10), nvl (Min_Price, 50))'. O su: 'nvl (NVL (Purchase_Price + (Purchase_Price * 0.10), Min_Price), 50)' :) –

+0

che è più veloce, le prestazioni saggia cosa dovrebbe essere usato? considerando migliaia di record da caricare? – rickyProgrammer

3

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

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

Questo è sbagliato, vedere di seguito. Gli argomenti possono essere tipi di dati diversi, ovvero documented: Se tutte le occorrenze di expr sono di tipo numerico o qualsiasi tipo di dati non numerico che può essere convertito implicitamente in un tipo di dati numerico, Oracle Database determina l'argomento con la precedenza numerica massima, converte implicitamente gli argomenti rimanenti in quel tipo di dati e restituisce quel tipo di dati.. In realtà questo è anche in contraddizione con l'espressione comune "COALESCE si arresta al primo verificarsi di un valore non Null", altrimenti il ​​caso di test n. 4 non dovrebbe generare un errore.

Anche secondo il caso di test n. 5 COALESCE esegue una conversione implicita degli 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: * Il 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. Arrestarsi al primo valore non nullo è un problema di runtime, non un problema in fase di compilazione. Al momento della compilazione il compilatore non sa che il terzo valore (diciamo) non sarà nullo; insiste sul fatto che il quarto argomento sia anche del tipo di dati corretto, anche se quel quarto valore non verrà mai effettivamente valutato. – mathguy

1

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

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

Esegui questo, quindi controllare my_sequence.currval;