NVL和Coalesce在Oracle中有什么不明显的区别吗?
显而易见的区别在于,coalesce将返回其参数列表中第一个非null项,而nvl只取两个参数,如果第一个不为null,则返回第一个,否则返回第二个。
似乎NVL只是coalesce的“基本情况”版本。
我有所遗漏吗?
COALESCE
是一个现代函数,是ANSI-92
标准的一部分。
NVL
是Oracle
特有的,在出现任何标准之前于80年代引入。
在两个值的情况下,它们是同义词。
但是,它们的实现方式不同。
NVL
始终计算两个参数,而COALESCE
通常在找到第一个非NULL
时停止计算(有一些例外,如序列NEXTVAL
):
SELECT SUM(val)
FROM (
SELECT NVL(1, LENGTH(RAWTOHEX(SYS_GUID()))) AS val
FROM dual
CONNECT BY
level <= 10000
)
由于生成 SYS_GUID()
,即使 1
不是 NULL
,此代码也要运行近 0.5
秒。
SELECT SUM(val)
FROM (
SELECT COALESCE(1, LENGTH(RAWTOHEX(SYS_GUID()))) AS val
FROM dual
CONNECT BY
level <= 10000
)
这意味着1
不是NULL
,因此不会评估第二个参数。
SYS_GUID
不需要生成,查询是即时的。
NVL会对第一个参数进行隐式转换,所以下面的代码不会出错
select nvl('a',sysdate) from dual;
COALESCE期望一致的数据类型。
select coalesce('a',sysdate) from dual;
会抛出“数据类型不一致错误”
NVL和COALESCE用于在列返回NULL的情况下提供默认值的相同功能。
它们之间的区别在于:
第三种情况的示例。其他情况很简单。
select nvl('abc',10) from dual;
将按预期工作,因为NVL将数字10隐式转换为字符串。
select coalesce('abc',10) from dual;
将失败并显示错误消息:“不一致的数据类型:预期CHAR,但得到了NUMBER”。
使用UNION的示例用法
SELECT COALESCE(a, sysdate)
from (select null as a from dual
union
select null as a from dual
);
发生错误:ORA-00932:数据类型不一致:期望CHAR类型,实际得到的是DATE类型
SELECT NVL(a, sysdate)
from (select null as a from dual
union
select null as a from dual
) ;
成功。更多信息:http://www.plsqlinformation.com/2016/04/difference-between-nvl-and-coalesce-in-oracle.html在计划处理方面也存在差异。
当搜索包含对具有索引列的nvl
结果进行比较时,Oracle可以通过连接分支过滤器来形成优化的计划。
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)
合并:
---------------------------------------------------------------------------------------
| 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))
感谢http://www.xt-r.com/2012/03/nvl-coalesce-concatenation.html提供的帮助。
coalesce()函数不会在第一个非空值处停止求值的另一个证明:
SELECT COALESCE(1, my_sequence.nextval) AS answer FROM dual;
my_sequence.currval;
。NVL:用指定的值来替换空值。
COALESCE:返回表达式列表中第一个非空表达式。
表格:PRICE_LIST
+----------------+-----------+
| Purchase_Price | Min_Price |
+----------------+-----------+
| 10 | null |
| 20 | |
| 50 | 30 |
| 100 | 80 |
| null | null |
+----------------+-----------+
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
+----------------+-----------+-----------------+----------------------+
| 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 |
+----------------+-----------+-----------------+----------------------+
NVL(Purchase_Price + (Purchase_Price * 0.10), nvl(Min_Price,50))
或者 nvl(NVL(Purchase_Price + (Purchase_Price * 0.10), Min_Price) ,50)
有什么看法? :) - Florin Ghita实际上,我不能完全同意每个说法。
"COALESCE期望所有参数都是相同的数据类型。"
这是错误的,请参见下面的说明。参数可以是不同的数据类型,这也在文档中有记录:如果所有的expr出现的次数都是数字数据类型或任何可以隐式转换为数字数据类型的非数字数据类型,则Oracle Database确定具有最高数字优先级的参数,隐式地将其余参数转换为该数据类型,并返回该数据类型。实际上,这甚至与常用表达式“COALESCE在第一个非Null值出现时停止”相矛盾,否则测试用例4不应该引发错误。
此外,根据测试用例5,COALESCE
会对参数进行隐式转换。
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!
虽然这个问题很明显,甚至是由提出这个问题的Tom以某种方式提到的。但我们再次提出。
NVL只能有2个参数。而Coalesce可以有多于两个参数。
select nvl('','',1) from dual;
//结果:ORA-00909
:无效的参数数量
select coalesce('','','1') from dual;
//输出:返回1