2009-06-04 25 views
158

¿Existen diferencias no obvias entre NVL y Coalesce en Oracle?Oracle Diferencias entre NVL y Coalesce

Las diferencias obvias son que coalesce devolverá el primer elemento no nulo en su lista de parámetros, mientras que nvl solo toma dos parámetros y devuelve el primero si no es nulo, de lo contrario, devuelve el segundo.

Parece que NVL podría ser simplemente una versión 'Caso Base" de coalescencia.

Me estoy perdiendo algo?

+0

Más aquí: https://jonathanlewis.wordpress.com/2018/02/13/coalesce-v-nvl/ –

Respuesta

249

COALESCE es la función más moderna que es una parte de ANSI-92 estándar.

NVL es Oracle específico, se introdujo en 80 antes de que hubiera alguna norma

En caso de dos valores, son sinónimos .

Sin embargo, se implementan de manera diferente.

NVL siempre evalúa ambos argumentos, mientras COALESCE lo general se detiene la evaluación cada vez que encuentra la primera no NULL (hay algunas excepciones, tales como secuencia NEXTVAL):

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

Este ejecuta por casi 0.5 segundos, desde genera SYS_GUID() 's, a pesar de 1 que no es un NULL.

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

Esto comprende que no es un 1NULL y no evalúa el segundo argumento.

SYS_GUID no se generan y la consulta es instantánea.

+2

Impresionante, gracias. Supuse que había algún tipo de truco. –

+6

No son exactamente sinónimos ... Al menos puede encontrar una diferencia en el hecho de que NVL realiza una conversión de tipo de datos implícito si los valores dados son de tipos diferentes. Entonces, por ejemplo, estaba obteniendo un error al usar COALESCE pasándole dos valores NULL (uno explícitamente establecido y el otro tomado de una columna en la base de datos, de tipo NUMBER), que simplemente desaparecen al cambiar la función a NVL. – DanielM

152

NVL hará una conversión implícita para el tipo de datos del primer parámetro, por lo que el siguiente no error

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

COALESCE espera tipos de datos consistentes.

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

arrojará un 'error de tipo de datos inconsistentes'

15

También hay diferencia está en el manejo del plan.

Oracle puede formar un plan optimizado con la concatenación de filtros de bifurcación cuando la búsqueda contiene una comparación del resultado nvl con una columna indexada.

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 unen:

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

Los créditos van a http://www.xt-r.com/2012/03/nvl-coalesce-concatenation.html.

3

Aunque esta es obvia, e incluso se menciona en cierto modo por Tom que hizo esta pregunta. Pero vamos a ponerlo de nuevo.

NVL solo puede tener 2 argumentos. Fundirse puede tener más de 2.

select nvl('','',1) from dual; // Resultado: ORA-00909: número no válido de argumentos
select coalesce('','','1') from dual; // salida: devuelve 1

13

NVL y se unen se utilizan para lograr la misma funcionalidad de proporcionar un valor por defecto en caso de que la columna devuelva un NULO.

Las diferencias son:

  1. NVL sólo acepta 2 argumentos mientras que COALESCE pueden tomar múltiples argumentos
  2. NVL evalúa tanto los argumentos y COALESCE detiene en primera aparición de un valor no nulo.
  3. NVL realiza una conversión de tipo de datos implícita basada en el primer argumento que se le ha asignado. COALESCE espera que todos los argumentos sean del mismo tipo de datos.
  4. COALESCE da problemas en consultas que usan cláusulas de UNION. Ejemplo debajo de
  5. COALESCE es un estándar ANSI donde NVL es específico de Oracle.

Ejemplos para la tercera caja. Otros casos son simples.

select nvl('abc',10) from dual; funcionaría como NVL hará una conversión implícita de 10 numéricos a la cadena.

select coalesce('abc',10) from dual; con el error - tipos de datos inconsistentes: se espera CHAR obtuvo el número

Ejemplo para UNION caso de uso

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

falla con 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 
    ) ; 

tiene éxito.

Más información: http://www.plsqlinformation.com/2016/04/difference-between-nvl-and-coalesce-in-oracle.html

1

NVL: Reemplazar la hipótesis nula con valor.

COALESCE: Devuelve la primera expresión no nula de la lista de expresiones.

Tabla: PRICE_LIST

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

A continuación se muestra el ejemplo de

[1] Conjunto precio de venta con la adición de 10% de beneficio a todos los productos.
[2] Si no hay un precio de lista de compras, entonces el precio de venta es el precio mínimo. Para venta de liquidación.
[3] Si no hay un precio mínimo también, establezca el precio de venta como el precio predeterminado "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 

Explicar con la vida real ejemplo práctico.

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

Se puede ver que con NVL podemos lograr normas [1], [2]
Pero con COALSECE podemos lograr las tres reglas.

+0

lo que dice sobre 'NVL (Purchase_Price + (Purchase_Price * 0.10), nvl (Min_Price, 50))'. O sobre: ​​'nvl (NVL (Purchase_Price + (Purchase_Price * 0.10), Min_Price), 50)' :) –

+0

que es más rápido, en cuanto a rendimiento, ¿qué se debe usar? teniendo en cuenta miles de registros para cargar? – rickyProgrammer

3

En realidad, no puedo aceptar cada afirmación.

"COALESCE espera que todos los argumentos sean del mismo tipo de datos".

Esto está mal, ver a continuación. Los argumentos pueden ser de tipos de datos diferentes, también documented: Si todas las ocurrencias de expr son un tipo de datos numéricos o cualquier tipo de datos no numéricos que se pueden convertir implícitamente a un tipo de datos numéricos, Oracle Database determina el argumento con la precedencia numérica más alta. convierte implícitamente los argumentos restantes a ese tipo de datos y devuelve ese tipo de datos.. En realidad, esto está incluso en contradicción con la expresión común "COALESCE se detiene en la primera aparición de un valor no nulo", de lo contrario, el caso de prueba n.º 4 no debería generar un error.

También de acuerdo con el caso de prueba n. ° 5 COALESCE realiza una conversión implícita de argumentos.

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: * La prueba 4 contradice "COALESCE detiene la evaluación en el primer valor no nulo" *. Estoy en desacuerdo. La prueba 4 muestra que el compilador verifica la coherencia del tipo de datos con COALESCE. La detención en el primer valor no nulo es un problema de tiempo de ejecución, no un problema de tiempo de compilación. En tiempo de compilación, el compilador no sabe que el tercer valor (por ejemplo) no será nulo; insiste en que el cuarto argumento sea también del tipo de datos correcto, incluso si ese cuarto valor nunca se evaluará realmente. – mathguy

1

Otra prueba de que aglutinan() no deja de evaluación con el primer valor no nulo:

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

Ejecutar este, a continuación, comprobar my_sequence.currval;