2009-06-04 14 views
158

Existe-t-il des différences non évidentes entre NVL et Coalesce dans Oracle? Les différences évidentes sont que coalesce retourne 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 renvoie le second.Oracle Différences entre NVL et Coalesce

Il semble que NVL peut juste être une « Version du cas de base » de soudent.

Suis-je manque quelque chose?

+0

Plus ici: https://jonathanlewis.wordpress.com/2018/02/13/coalesce-v-nvl/ –

Répondre

249

COALESCE est fonction plus moderne qui est une partie de ANSI-92 standard.

NVL est Oracle spécifique, il a été introduit dans 80

Dans le cas de deux valeurs, ils sont synonymes .

Cependant, ils sont implémentés différemment.

NVL Evalue toujours deux arguments, alors que COALESCE arrête généralement l'évaluation chaque fois qu'il trouve la première NULL non (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 va pour presque 0.5 secondes, depuis il génère SYS_GUID(), bien que 1 ne soit pas NULL.

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

Cela comprend que 1 n'est pas un NULL et n'évalue pas le second argument.

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

+2

Génial, merci. Je devinais qu'il y avait une sorte de tour. –

+6

Ils ne sont pas exactement des synonymes ... Au moins, vous pouvez trouver une différence dans le fait que NVL effectue un cast 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 passant deux valeurs NULL (l'une explicitement définie et l'autre tirée d'une colonne de la base de données, de type NUMBER), qui disparaissent simplement en changeant la fonction en NVL. – DanielM

152

NVL va faire une conversion implicite du type de données du premier paramètre, les indications suivantes ne pas l'erreur

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

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

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

jetteront une «erreur de type de données incohérent

15

Il y a aussi la différence est dans la gestion du régime.

Oracle est capable de former un plan optimisé avec concaténation de 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) 

soudent:

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

Crédits vont à http://www.xt-r.com/2012/03/nvl-coalesce-concatenation.html.

3

Bien que celui-ci soit évident, et même mentionné d'une manière mis en place par Tom qui a posé cette question. Mais permet de mettre en place à nouveau. NVL ne peut avoir que deux arguments:

Coalesce peut avoir plus de 2.

select nvl('','',1) from dual; // Résultat: ORA-00909: nombre incorrect d'arguments
select coalesce('','','1') from dual; // sortie: retourne 1

13

NVL et COALESCE sont utilisés pour obtenir la même fonctionnalité de fournir une valeur par défaut dans le cas où la colonne renvoie une valeur NULL.

Les différences sont les suivantes:

  1. NVL accepte uniquement deux arguments, alors que COALESCE peuvent prendre de multiples arguments
  2. NVL évalue à la fois les arguments et les COALESCE arrête à la première occurrence d'une valeur non nulle. NVL effectue une conversion de type de données implicite basée sur le premier argument qui lui est attribué. COALESCE s'attend à ce que tous les arguments soient du même type de données.
  3. COALESCE donne des problèmes dans les requêtes qui utilisent des clauses UNION. Exemple ci-dessous
  4. 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 de 10 numérique en chaîne.

select coalesce('abc',10) from dual; échouera avec l'erreur - types de données incompatibles: attendu CHAR obtenu NUMÉRO

Exemple UNION cas d'utilisation

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

1

NVL: Remplacez la valeur nulle avec la 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  | 
+----------------+-----------+ 

Voici l'exemple de

Set prix de vente [1] avec l'ajout de bénéfice de 10% à tous les produits.
[2] S'il n'y a pas de prix catalogue, alors le prix de vente est le prix minimum. 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 

Expliquer avec la vie réelle exemple pratique.

+----------------+-----------+-----------------+----------------------+ 
| 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 le voir avec NVL nous pouvons parvenir à des règles [1], [2]
Mais avec COALSECE nous pouvons atteindre les trois règles.

+0

ce que vous dites à propos de 'NVL (Purchase_Price + (Purchase_Price * 0.10), nvl (Min_Price, 50))'. Ou à propos de: 'nvl (NVL (Purchase_Price + (Purchase_Price * 0.10), Min_Price), 50)' :) –

+0

qui est plus rapide, la performance sage ce qui devrait être utilisé? compte tenu de milliers d'enregistrements à charger? – rickyProgrammer

3

En fait, je ne peux pas accepter chaque énoncé.

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

Ceci est faux, voir ci-dessous. Les arguments peuvent être des types de données différents, c'est-à-dire documented: Si toutes les occurrences de expr sont des types de données numériques ou des types de données non numériques pouvant être implicitement convertis en type de données numériques, Oracle Database détermine l'argument ayant la plus haute priorité numérique, convertit implicitement 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-test n ° 4 ne devrait pas générer d'erreur.

Également selon le cas de test n ° 5 COALESCE fait une conversion implicite des 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! 
+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 sur le fait 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

1

Une autre preuve que coalescent() ne cesse pas d'évaluation avec la première valeur non nulle:

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

Exécuter ce, puis vérifiez my_sequence.currval;

Questions connexes