web-dev-qa-db-fra.com

Différences Oracle entre NVL et Coalesce

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 null dans sa liste de paramètres, tandis que nvl ne prend que deux paramètres et renvoie le premier si ce n'est pas null, sinon il renvoie le second.

Il semble que NVL ne soit peut-être qu'une version de "coalesce" du "cas de base".

Est-ce que je manque quelque chose?

192
Tom Hubbard

COALESCE est une fonction plus moderne faisant partie de la norme ANSI-92.

NVL est spécifique à Oracle, il a été introduit dans 80 avant la mise en place de normes.

Dans le cas de deux valeurs, elles sont synonymes.

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

NVL évalue toujours les deux arguments, tandis que COALESCE arrête généralement l'évaluation dès qu'il trouve le premier non -NULL (il existe quelques exceptions, telles que 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 presque 0.5 _ secondes, car il génère SYS_GUID(), bien que 1 ne soit pas un NULL.

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

Ceci comprend que 1 n'est pas un NULL et n'évalue pas le deuxième argument.

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

289
Quassnoi

NVL effectuera une conversion implicite vers le type de données du premier paramètre. Par conséquent, les erreurs suivantes ne se produisent pas.

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

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

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

va jeter une 'erreur de type de données incohérente'

164
Gary Myers

NVL et COALESCE permettent d'obtenir la même fonctionnalité, à savoir fournir une valeur par défaut au cas où la colonne renverrait une valeur NULL.

Les différences sont:

  1. NVL n'accepte que 2 arguments alors que COALESCE peut prendre 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 implicite de type de données en fonction du premier argument qui lui est attribué. 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 où NVL est spécifique à Oracle.

Exemples pour le troisième cas. Les autres cas sont simples.

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

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

Exemple pour le 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

19
Brahmareddy K

Il y a aussi une différence dans la gestion des plans.

Oracle peut créer un plan optimisé avec concaténation des filtres de branche lorsque la recherche contient une comparaison du résultat nvl 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 vont à http://www.xt-r.com/2012/03/nvl-coalesce-concatenation.html .

16
Vadzim

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, c'est aussi documenté : Si toutes les occurrences de expr sont des types de données numériques ou tout type de données non numérique qui peut être converti implicitement en numérique type de données, Oracle Database détermine l’argument ayant la priorité numérique la plus élevée, convertit implicitement les arguments restants en ce type de données et renvoie ce type de données . En réalité, cela est même en contradiction avec l'expression courante "COALESCE s'arrête à la première occurrence d'une valeur non Null", sinon le test élémentaire n ° 4 ne devrait pas générer d'erreur.

Toujours selon le scénario de test n ° 5, COALESCE effectue une conversion implicite d'arguments.

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!
5

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;

4
Herb Swift

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 permet de mettre en place à nouveau.

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

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

3
Neel

NVL: Remplacez la valeur null par la valeur.

COALESCE: Renvoie la première expression non NULL de la liste d'expressions.

Tableau: PRICE_LIST

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

Voici 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 sur la liste, le prix de vente est le prix minimal. Pour la vente de 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 des exemples concrets 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 des règles [1], [2]
Mais avec COALSECE, nous pouvons respecter les trois règles.

2
sandip