Différences Oracle entre NVL et Coalesce


208

Existe-t-il des différences non évidentes entre NVL et Coalesce dans Oracle?

Les différences évidentes sont que coalesce renverra le premier élément non nul dans sa liste de paramètres alors que nvl ne prend que deux paramètres et renvoie le premier s'il n'est pas nul, sinon il retourne le second.

Il semble que NVL soit juste une version «de base» de la fusion.

Suis-je en train de manquer quelque chose?


Réponses:


312

COALESCEest une fonction plus moderne qui fait partie de la ANSI-92norme.

NVLest Oraclespécifique, il a été introduit dans 80's avant qu'il n'y ait des normes.

Dans le cas de deux valeurs, ce sont des synonymes.

Cependant, ils sont mis en œuvre différemment.

NVLévalue toujours les deux arguments, mais COALESCEarrête généralement l'évaluation chaque fois qu'il trouve le premier non NULL(il y a quelques exceptions, comme la séquence NEXTVAL):

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

Cela fonctionne pendant près de 0.5secondes, car il génère SYS_GUID()des, bien 1qu'il ne soit pas un NULL.

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

Cela comprend que ce 1n'est pas un NULLet n'évalue pas le deuxième argument.

SYS_GUIDne sont pas générés et la requête est instantanée.


11
Ce ne sont pas exactement des synonymes ... Au moins, vous pouvez trouver une différence dans le fait que NVL effectue une conversion de type de données implicite si les valeurs données sont de types différents. Ainsi, par exemple, j'obtenais une erreur en utilisant COALESCE en lui transmettant deux valeurs NULL (l'une définie explicitement et l'autre extraite d'une colonne de la base de données, de type NUMBER), qui disparaissent simplement en modifiant la fonction en NVL.
DanielM

170

NVL effectuera une conversion implicite vers le type de données du premier paramètre, donc ce qui suit ne fait pas d'erreur

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

COALESCE attend des types de données cohérents.

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

générera une "erreur de type de données incohérent"


22

NVL et COALESCE sont utilisés pour obtenir la même fonctionnalité de fourniture d'une valeur par défaut au cas où la colonne renvoie un NULL.

Les différences sont les suivantes:

  1. NVL n'accepte que 2 arguments alors que COALESCE peut accepter plusieurs arguments
  2. NVL évalue les arguments et COALESCE s'arrête à la première occurrence d'une valeur non Null.
  3. NVL effectue une conversion de type de données implicite basée sur le premier argument qui lui est donné. COALESCE s'attend à ce que tous les arguments soient du même type de données.
  4. COALESCE donne des problèmes dans les requêtes qui utilisent des clauses UNION. Exemple ci-dessous
  5. COALESCE est la norme ANSI alors que NVL est spécifique à Oracle.

Exemples pour le troisième cas. D'autres cas sont simples.

select nvl('abc',10) from dual; fonctionnerait car NVL effectuera une conversion implicite du numérique 10 en chaîne.

select coalesce('abc',10) from dual; échouera avec Erreur - types de données incohérents: CHAR attendu a NUMBER

Exemple de cas d'utilisation UNION

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

échoue avec 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
      ) ;

réussit.

Plus d'informations: http://www.plsqlinformation.com/2016/04/difference-between-nvl-and-coalesce-in-oracle.html


Je ne pense pas qu'il y ait un problème spécifique avec "union" tant il semble qu'Oracle veuille taper cast null dans votre sous-requête à un caractère par défaut et vous avez alors le même problème répertorié dans votre article 3 (données mixtes les types). Si vous le changez en TO_DATE (NULL), vous n'obtiendrez probablement pas l'erreur (je ne peux pas reproduire l'erreur sur la version d'Oracle que j'utilise). Sinon, je suis d'accord et apprécie votre réponse. :-)
splashout

17

Il existe également une différence dans la gestion des plans.

Oracle est capable de former un plan optimisé avec concaténation de filtres de branche lorsque la recherche contient une comparaison des nvlrésultats avec une colonne indexée.

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)

se fondre:

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

Les crédits sont disponibles sur http://www.xt-r.com/2012/03/nvl-coalesce-concatenation.html .


6

Une autre preuve que coalesce () n'arrête pas l'évaluation avec la première valeur non nulle:

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

Exécutez ceci, puis vérifiez my_sequence.currval;


5

En fait, je ne peux pas accepter chaque déclaration.

"COALESCE s'attend à ce que tous les arguments soient du même type de données."

C'est faux, voir ci-dessous. Les arguments peuvent être différents types de données, ce qui est également documenté : si toutes les occurrences de expr sont des types de données numériques ou tout type de données non numériques qui peuvent être implicitement convertis en un type de données numériques, Oracle Database détermine alors l'argument ayant la priorité numérique la plus élevée, implicitement convertit les arguments restants en ce type de données et renvoie ce type de données. . En fait, cela est même en contradiction avec l'expression courante "COALESCE s'arrête à la première occurrence d'une valeur non nulle", sinon le cas de test n ° 4 ne devrait pas générer d'erreur.

Toujours selon le cas de test n ° 5, COALESCEune conversion implicite des arguments est effectuée.

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: Le test 4 contredit "COALESCE arrête l'évaluation à la première valeur non nulle" . Je ne suis pas d'accord. Le test 4 montre que le compilateur vérifie la cohérence du type de données avec COALESCE. L'arrêt à la première valeur non nulle est un problème d'exécution, pas un problème de compilation. Au moment de la compilation, le compilateur ne sait pas que la troisième valeur (disons) sera non nulle; il insiste pour que le quatrième argument soit également du bon type de données, même si cette quatrième valeur ne sera jamais réellement évaluée.
mathguy

3

Bien que celui-ci soit évident, et même mentionné d'une manière présentée par Tom qui a posé cette question. Mais reprenons.

NVL ne peut avoir que 2 arguments. Coalesce peut en avoir plus de 2.

select nvl('','',1) from dual;// Résultat:: ORA-00909nombre d'arguments invalide
select coalesce('','','1') from dual; // Sortie: renvoie 1


3

NVL: remplacez le null par une valeur.

COALESCE: renvoie la première expression non nulle de la liste d'expressions.

Tableau: PRICE_LIST

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

Vous trouverez ci-dessous l'exemple de

[1] Fixer le prix de vente en ajoutant 10% de profit à tous les produits.
[2] S'il n'y a pas de prix d'achat, le prix de vente est le prix minimum. Pour vente en liquidation.
[3] S'il n'y a pas de prix minimum également, définissez le prix de vente comme prix par défaut "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

Expliquez avec un exemple pratique de la vie réelle.

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

Vous pouvez voir qu'avec NVL, nous pouvons atteindre les règles [1], [2]
Mais avec COALSECE, nous pouvons atteindre les trois règles.


ce que vous dites NVL(Purchase_Price + (Purchase_Price * 0.10), nvl(Min_Price,50)) . Ou environ: nvl(NVL(Purchase_Price + (Purchase_Price * 0.10), Min_Price) ,50) :)
Florin Ghita

qui est plus rapide, en termes de performances, que faut-il utiliser? considérant des milliers d'enregistrements à charger?
rickyProgrammer
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.