Oracle Unterschiede zwischen NVL und Coalesce


207

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, während nvl nur zwei Parameter verwendet und den ersten zurückgibt, wenn es nicht Null ist, andernfalls den zweiten.

Es scheint, dass NVL nur eine "Base Case" -Version von Coalesce ist.

Vermisse ich etwas


Antworten:


311

COALESCEist eine modernere Funktion, die Teil des ANSI-92Standards ist.

NVList Oraclespezifisch, es wurde in eingeführt 80, bevor es irgendwelche Standards gab.

Bei zwei Werten handelt es sich um Synonyme.

Sie sind jedoch unterschiedlich implementiert.

NVLwertet immer beide Argumente aus, während COALESCEnormalerweise die Auswertung gestoppt wird, wenn das erste Nicht- gefunden wird NULL(es gibt einige Ausnahmen, wie z. B. die Reihenfolge NEXTVAL):

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

Dies läuft fast 0.5Sekunden, da es SYS_GUID()'s generiert , obwohl 1es kein a ist NULL.

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

Dies versteht, dass dies 1kein a ist NULLund bewertet das zweite Argument nicht.

SYS_GUIDwerden nicht generiert und die Abfrage erfolgt sofort.


11
Sie sind nicht gerade Synonyme ... Zumindest können Sie einen Unterschied darin feststellen, dass NVL ein implizites Casting von Datentypen durchführt, wenn die angegebenen Werte unterschiedlichen Typs sind. So wurde beispielsweise bei der Verwendung von COALESCE ein Fehler angezeigt, bei dem zwei NULL-Werte (einer explizit festgelegt und der andere aus einer Spalte in der Datenbank vom Typ NUMBER) übergeben wurden, die einfach durch Ändern der Funktion in NVL verschwinden.
DanielM

170

NVL führt eine implizite Konvertierung in den Datentyp des ersten Parameters durch, sodass der folgende Fehler nicht auftritt

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

COALESCE erwartet konsistente Datentypen.

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

wird einen 'inkonsistenten Datentypfehler' auslösen


22

NVL und COALESCE werden verwendet, um die gleiche Funktionalität wie bei der Bereitstellung eines Standardwerts zu erreichen, falls die Spalte NULL zurückgibt.

Die Unterschiede sind:

  1. NVL akzeptiert nur 2 Argumente, während COALESCE mehrere Argumente annehmen kann
  2. NVL wertet beide Argumente aus und COALESCE stoppt beim ersten Auftreten eines Nicht-Null-Werts.
  3. NVL führt eine implizite Datentypkonvertierung basierend auf dem ersten Argument durch. COALESCE erwartet, dass alle Argumente denselben Datentyp haben.
  4. COALESCE gibt Probleme bei Abfragen, die UNION-Klauseln verwenden. Beispiel unten
  5. COALESCE ist ein ANSI-Standard, wobei NVL Oracle-spezifisch ist.

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 von numerischen 10 in Zeichenfolgen durchführt.

select coalesce('abc',10) from dual; schlägt mit Fehler fehl - inkonsistente Datentypen: erwartet CHAR hat NUMBER

Beispiel für den 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


Ich glaube nicht, dass es ein spezifisches Problem mit "union" gibt, so sehr, dass Oracle standardmäßig cast null in Ihre Unterabfrage für ein Zeichen eingeben möchte, und dann haben Sie das gleiche Problem in Ihrem Punkt 3 (gemischte Daten) Typen). Wenn Sie es in TO_DATE (NULL) ändern, wird der Fehler wahrscheinlich nicht angezeigt (ich kann den Fehler in der von mir verwendeten Oracle-Version nicht reproduzieren). Ansonsten stimme ich Ihrer Antwort zu und schätze sie. :-)
Splashout

17

Es gibt auch Unterschiede in der Planabwicklung.

Oracle kann einen optimierten Plan mit Verkettung von Verzweigungsfiltern erstellen, wenn die Suche einen Vergleich des nvlErgebnisses 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 finden Sie unter http://www.xt-r.com/2012/03/nvl-coalesce-concatenation.html .


6

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ühren Sie dies aus und überprüfen Sie es my_sequence.currval;


5

Eigentlich kann ich nicht jeder Aussage zustimmen.

"COALESCE erwartet, dass alle Argumente denselben Datentyp haben."

Das ist falsch, siehe unten. Argumente können unterschiedliche Datentypen sein, was auch dokumentiert ist : Wenn alle Vorkommen von expr ein numerischer Datentyp oder ein nicht numerischer Datentyp sind, der implizit in einen numerischen Datentyp konvertiert werden kann, bestimmt Oracle Database implizit das Argument mit der höchsten numerischen Priorität konvertiert die verbleibenden Argumente in diesen Datentyp und gibt diesen Datentyp zurück. . Tatsächlich steht dies sogar im Widerspruch zum allgemeinen Ausdruck "COALESCE stoppt beim ersten Auftreten eines Nicht-Null-Werts", andernfalls sollte Testfall Nr. 4 keinen Fehler auslösen.

Auch nach Testfall Nr. 5 COALESCEerfolgt eine implizite Konvertierung von Argumenten.

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
Betreff : Test 4 widerspricht "COALESCE stoppt die Auswertung beim ersten Nicht-Null-Wert" . Ich bin nicht einverstanden. Test 4 zeigt, dass der Compiler die Konsistenz des Datentyps mit COALESCE überprüft. Das Anhalten beim ersten Wert ungleich Null ist ein Laufzeitproblem und kein Problem zur Kompilierungszeit. Zur Kompilierungszeit weiß der Compiler nicht, dass der dritte Wert (z. B.) nicht null ist. es besteht darauf, dass das vierte Argument auch vom richtigen Datentyp ist, selbst wenn dieser vierte Wert niemals tatsächlich ausgewertet wird.
Mathguy

3

Obwohl dies offensichtlich ist und sogar von Tom erwähnt wird, der diese Frage gestellt hat. 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-00909ungültige Anzahl von Argumenten
select coalesce('','','1') from dual; // Ausgabe: gibt 1 zurück


3

NVL: Ersetzen Sie die Null durch den 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 sehen Sie das Beispiel für

[1] Festlegen des Verkaufspreises mit einem Gewinn von 10% für alle Produkte.
[2] Wenn es keinen Kauflistenpreis gibt, ist der Verkaufspreis der Mindestpreis. Für den Ausverkauf.
[3] Wenn es auch keinen Mindestpreis gibt, setzen Sie den Verkaufspreis als Standardpreis "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

Erklären Sie anhand eines praktischen Beispiels aus dem wirklichen Leben.

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


was du darüber sagst NVL(Purchase_Price + (Purchase_Price * 0.10), nvl(Min_Price,50)) . Oder über: nvl(NVL(Purchase_Price + (Purchase_Price * 0.10), Min_Price) ,50) :)
Florin Ghita

Was ist schneller, was die Leistung betrifft, was sollte verwendet werden? Betrachten Sie Tausende von Datensätzen zum Laden?
RickyProgrammer
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.