Oracle与NVL和合并的区别

在Oracle中NVL和Coalesce之间有没有明显的区别?

明显的区别在于coalesce将返回参数列表中的第一个非null项目,而nvl只接受两个参数,如果不是null则返回第一个参数,否则返回第二个参数。

NVL似乎只是一个“Base Case”版本的合并。

我错过了什么吗?

COALESCE是更现代化的function,是ANSI-92标准的一部分。

NVLOracle特有的,它在80年代就有了标准。

在两个值的情况下,它们是同义词。

但是,它们的实施方式不同。

NVL总是评估两个参数,而COALESCE只要发现第一个非NULL就停止评估:

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

这会运行近0.5秒,因为它会生成SYS_GUID() ,尽pipe1不是NULL

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

这理解为1不是NULL并且不计算第二个参数。

没有生成SYS_GUID ,查询是即时的。

NVL将对第一个参数的数据types进行隐式转换,所以下面的内容不会出错

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

COALESCE期望一致的数据types。

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

会抛出一个“不一致的数据types错误”

在计划处理方面也有不同之处。

当search包含nvl结果与索引列的比较时,Oracle能够形成一个连接分支filter的优化计划。

 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

NVL和COALESCE用于实现提供默认值以防列返回NULL的相同function。

差异是:

  1. NVL只接受2个参数,而COALESCE可以接受多个参数
  2. NVL在首次出现非Null值时评估参数和COALESCE停止。
  3. NVL根据给定的第一个参数进行隐式数据types转换。 COALESCE期望所有的参数是相同的数据types。
  4. COALESCE在使用UNION子句的查询中给出了问题。 下面的例子
  5. COALESCE是ANSI标准,其中NVL是Oracle特定的。

第三种情况的例子。 其他情况很简单。

select nvl('abc',10) from dual; 将工作,因为NVL将执行数字10到string的隐式转换。

select coalesce('abc',10) from dual; 将会失败,错误 – 不一致的数据types:预期的CHAR有NUMBER

UNION用例的例子

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

ORA-00932: inconsistent datatypes: expected CHAR got DATE失败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 ) ; 

成功。

更多信息: http : //www.plsqlinformation.com/2016/04/difference-between-nvl-and-coalesce-in-oracle.html

虽然这个问题很明显,甚至被汤姆提出来提出这个问题。 但让我们再次提出。

NVL只能有2个参数。 合并可能有2个以上。

select nvl('','',1) from dual; //结果: ORA-00909 :参数的数目无效
select coalesce('','','1') from dual; //输出:返回1

其实我不能同意每一个声明。

“COALESCE期望所有的参数是相同的数据types”。

这是错误的,见下文。 参数可以是不同的数据types,这也是logging的 : 如果所有出现的expr都是数字数据types或任何可以隐式转换为数字数据types的非数字数据types,则Oracle数据库将隐式地确定具有最高数字优先级的参数将剩余的参数转换为该数据types,并返回该数据types。 。 实际上,这甚至与“毫无价值的第一次发生时停止”的通常expression是矛盾的,否则testing用例4不应该提出错误。

另外根据第五个testing案例, 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! 

NVL:用值replacenull。

COALESCE:从expression式列表中返回第一个非空expression式。

表格:PRICE_LIST

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

下面是例子

[1]设置销售价格,所有产品增加10%的利润。
[2]如果没有购买清单价格,则销售价格是最低价格。 为清除销售。
[3]如果没有最低价格,则将销售价格设置为默认价格“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 

用现实生活中的实际例子来解释。

 +----------------+-----------+-----------------+----------------------+ | 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我们可以实现规则[1],[2]
但是通过COALSECE,我们可以实现所有三个规则。