Diferencias de Oracle entre NVL y Coalesce
¿Hay diferencias no obvias entre NVL y Coalesce en Oracle?
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 puede ser solo una versión 'Caso Base" de coalesce.
¿Me estoy perdiendo algo?
8 answers
COALESCE
es una función más moderna que forma parte del estándar ANSI-92
.
NVL
es Oracle
específico, se introdujo en 80
's antes de que hubiera ningún estándar.
En el caso de dos valores, son sinónimos.
Sin embargo, se implementan de manera diferente.
NVL
siempre evalúa ambos argumentos, mientras que COALESCE
generalmente detiene la evaluación cada vez que encuentra el primer no-NULL
(hay algunas excepciones, como la secuencia NEXTVAL
):
SELECT SUM(val)
FROM (
SELECT NVL(1, LENGTH(RAWTOHEX(SYS_GUID()))) AS val
FROM dual
CONNECT BY
level <= 10000
)
Esto se ejecuta para casi 0.5
segundos, ya que genera SYS_GUID()
's, a pesar de 1
no ser un NULL
.
SELECT SUM(val)
FROM (
SELECT COALESCE(1, LENGTH(RAWTOHEX(SYS_GUID()))) AS val
FROM dual
CONNECT BY
level <= 10000
)
Esto entiende que 1
no es un NULL
y no evalúa el segundo argumento.
SYS_GUID
's no se generan y la consulta es instantánea.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-02-13 20:53:01
NVL hará una conversión implícita al tipo de datos del primer parámetro, por lo que lo siguiente no produce error
select nvl('a',sysdate) from dual;
COALESCE espera tipos de datos consistentes.
select coalesce('a',sysdate) from dual;
Lanzará un'error de tipo de datos inconsistente'
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-06-05 05:13:19
También hay diferencia en el manejo del plan.
Oracle puede formar un plan optimizado con concatenación de filtros de rama cuando la búsqueda contiene la 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 .
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-10-07 18:15:18
NVL y COALESCE se utilizan para lograr la misma funcionalidad de proporcionar un valor predeterminado en caso de que la columna devuelva un NULL.
Las diferencias son:
- NVL acepta solo 2 argumentos mientras que COALESCE puede tomar múltiples argumentos
- NVL evalúa tanto los argumentos como las paradas de COALESCE al principio ocurrencia de un valor no nulo.
- NVL realiza una conversión de tipo de datos implícita basada en el primer argumento dado a ella. COALESCE espera que todos los argumentos sean de la misma tipo de datos.
- COALESCE da problemas en las consultas que utilizan cláusulas de UNIÓN. Ejemplo abajo
- COALESCE es un estándar ANSI donde as NVL es específico de Oracle.
Ejemplos para el tercer caso. Otros casos son simples.
select nvl('abc',10) from dual;
funcionaría como NVL hará una conversión implícita de 10 numérico a cadena.
select coalesce('abc',10) from dual;
fallará con tipos de datos inconsistentes de error: CHAR esperado número obtenido
Ejemplo de caso de uso de la UNIÓN
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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-03-09 20:54:18
Otra prueba de que coalesce() no detiene la evaluación con el primer valor no nulo:
SELECT COALESCE(1, my_sequence.nextval) AS answer FROM dual;
Ejecute esto, luego verifique my_sequence.currval;
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-02-13 21:37:21
Aunque esta es obvia, e incluso mencionada de una manera planteada por Tom quien hizo esta pregunta. Pero vamos a poner de nuevo.
NVL puede tener solo 2 argumentos. Coalesce puede tener más de 2.
select nvl('','',1) from dual;
//Resultado: ORA-00909
: número incorrecto de argumentosselect coalesce('','','1') from dual;
//Salida: devuelve 1
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-11-19 06:04:57
En realidad no puedo estar de acuerdo con cada declaración.
"COALESCE espera que todos los argumentos sean del mismo tipo de datos."
Esto está mal, ver más abajo. Los argumentos pueden ser diferentes tipos de datos, que también se documenta : Si todas las ocurrencias de expr son tipo de datos numéricos o cualquier tipo de datos no numéricos que se puede 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 No.4 no debería generar un error.
También de acuerdo con el caso de prueba No.5 COALESCE
hace 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!
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-11-30 08:59:24
NVL: Reemplaza el valor null por 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] Establecer el precio de venta con la adición de 10% de beneficio a todos los productos.
[2] Si no hay precio de lista de compra, entonces el precio de venta es el precio mínimo. Para la venta de liquidación.
[3] Si no hay un precio mínimo también, a continuación, establecer el precio de venta como precio por defecto "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 ejemplo práctico de la vida real.
+----------------+-----------+-----------------+----------------------+
| 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 |
+----------------+-----------+-----------------+----------------------+
Puedes ver que con NVL podemos lograr reglas [1],[2]
Pero con COALSECE podemos lograr las tres reglas.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-01-29 09:42:51