Gibt es nicht offensichtliche Unterschiede zwischen NVL und Coalesce in Oracle?
Die offensichtlichen Unterschiede bestehen darin, dass Coalesce das erste Nicht-Null-Element in seiner Parameterliste zurückgibt, wohingegen nvl nur zwei Parameter akzeptiert und das erste zurückgibt, wenn es nicht Null ist, andernfalls das zweite.
Es scheint, dass NVL nur eine "Base Case" -Version von Coalesce ist.
Vermisse ich etwas?
COALESCE
ist eine modernere Funktion, die Teil des Standards ANSI-92
ist.
NVL
ist Oracle
spezifisch, es wurde in 80
eingeführt, bevor es Standards gab.
Bei zwei Werten handelt es sich um Synonyme.
Sie sind jedoch unterschiedlich implementiert.
NVL
wertet immer beide Argumente aus, während COALESCE
normalerweise die Auswertung stoppt, wenn das erste Nicht-NULL
gefunden wird (es gibt einige Ausnahmen, wie z. B. die Sequenz NEXTVAL
) :
SELECT SUM(val)
FROM (
SELECT NVL(1, LENGTH(RAWTOHEX(SYS_GUID()))) AS val
FROM dual
CONNECT BY
level <= 10000
)
Dies dauert fast 0.5
Sekunden, da es SYS_GUID()
generiert, obwohl 1
Kein NULL
ist.
SELECT SUM(val)
FROM (
SELECT COALESCE(1, LENGTH(RAWTOHEX(SYS_GUID()))) AS val
FROM dual
CONNECT BY
level <= 10000
)
Dies versteht, dass 1
Kein NULL
ist und das zweite Argument nicht auswertet.
SYS_GUID
Werden nicht generiert und die Abfrage erfolgt sofort.
NVL führt eine implizite Konvertierung in den Datentyp des ersten Parameters durch, sodass Folgendes nicht fehlerhaft ist
select nvl('a',sysdate) from dual;
COALESCE erwartet konsistente Datentypen.
select coalesce('a',sysdate) from dual;
wird einen inkonsistenten Datentypfehler auslösen
NVL und COALESCE werden verwendet, um die gleiche Funktionalität wie beim Bereitstellen eines Standardwerts für den Fall zu erzielen, dass die Spalte NULL zurückgibt.
Die Unterschiede sind:
Beispiele für den dritten Fall. Andere Fälle sind einfach.
select nvl('abc',10) from dual;
würde funktionieren, da NVL eine implizite Konvertierung der Zahl 10 in einen String vornimmt.
select coalesce('abc',10) from dual;
schlägt mit fehlerhaft inkonsistenten Datentypen fehl: erwarteter CHAR hat NUMBER
Beispiel für einen UNION-Anwendungsfall
SELECT COALESCE(a, sysdate)
from (select null as a from dual
union
select null as a from dual
);
scheitert mit 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
) ;
gelingt es.
Weitere Informationen: http://www.plsqlinformation.com/2016/04/difference-between-nvl-and-coalesce-in-Oracle.html
Es gibt auch Unterschiede in der Planabwicklung.
Oracle kann einen optimierten Plan mit Verkettung von Verzweigungsfiltern erstellen, wenn die Suche den Vergleich von nvl
Ergebnissen mit einer indizierten Spalte enthält.
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)
verschmelzen:
---------------------------------------------------------------------------------------
| 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))
Credits gehen an http://www.xt-r.com/2012/03/nvl-coalesce-concatenation.html .
Eigentlich kann ich nicht jeder Aussage zustimmen.
"COALESCE erwartet, dass alle Argumente denselben Datentyp haben."
Das ist falsch, siehe unten. Argumente können verschiedene Datentypen sein, das heißt auch dokumentiert : Wenn alle Vorkommen von expr numerische Datentypen oder beliebige nicht numerische Datentypen sind, die implizit in numerische Datentypen konvertiert werden können Datentyp, dann bestimmt Oracle Database das Argument mit der höchsten numerischen Priorität, konvertiert implizit die verbleibenden Argumente in diesen Datentyp und gibt diesen Datentyp zurück. . Tatsächlich widerspricht dies sogar dem allgemeinen Ausdruck "COALESCE stoppt beim ersten Auftreten eines Nicht-Null-Wertes", da sonst der Testfall Nr. 4 keinen Fehler auslösen sollte.
Auch gemäß Testfall Nr. 5 führt COALESCE
eine implizite Umwandlung von Argumenten durch.
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!
Ein weiterer Beweis dafür, dass coalesce () die Auswertung mit dem ersten Nicht-Null-Wert nicht beendet:
SELECT COALESCE(1, my_sequence.nextval) AS answer FROM dual;
Führe dies aus und überprüfe my_sequence.currval;
Obwohl dieser offensichtlich ist und sogar auf eine Weise erwähnt wird, die von Tom aufgestellt wurde, der diese Frage stellte. Aber lassen Sie uns noch einmal aufstellen.
NVL kann nur 2 Argumente haben. Coalesce kann mehr als 2 haben.
select nvl('','',1) from dual;
// Ergebnis: ORA-00909
: ungültige Anzahl von Argumentenselect coalesce('','','1') from dual;
// Ausgabe: gibt 1 zurück
NVL: Ersetzen Sie die Null durch einen Wert.
COALESCE: Gibt den ersten Nicht-Null-Ausdruck aus der Ausdrucksliste zurück.
Tabelle: PRICE_LIST
+----------------+-----------+
| Purchase_Price | Min_Price |
+----------------+-----------+
| 10 | null |
| 20 | |
| 50 | 30 |
| 100 | 80 |
| null | null |
+----------------+-----------+
Unten ist das Beispiel von
[1] Legen Sie den Verkaufspreis fest, indem Sie 10% Gewinn zu allen Produkten hinzufügen.
[2] Wenn es keinen Kauflistenpreis gibt, ist der Verkaufspreis der Mindestpreis. Für den Ausverkauf.
[3] Wenn es auch keinen Mindestpreis gibt, legen Sie den Verkaufspreis als Standardpreis "50" fest.
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
Erklären Sie dies anhand eines praktischen Beispiels.
+----------------+-----------+-----------------+----------------------+
| 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 |
+----------------+-----------+-----------------+----------------------+
Sie können sehen, dass wir mit NVL Regeln erreichen können [1], [2]
Aber mit COALSECE können wir alle drei Regeln erreichen.