NVL与Coalesce之间的Oracle差异

时间:2009-06-04 11:58:55

标签: sql oracle coalesce nvl

Oracle中的NVL和Coalesce之间是否存在明显差异?

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

似乎NVL可能只是合并的“基本案例”版本。

我错过了什么吗?

8 个答案:

答案 0 :(得分:285)

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
        )

此约为0.5秒运行,因为它生成SYS_GUID(),尽管1不是NULL

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

这理解1不是NULL并且不评估第二个参数。

SYS_GUID未生成,查询是即时的。

答案 1 :(得分:163)

NVL将隐式转换为第一个参数的数据类型,因此以下内容不会出错

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

COALESCE期望一致的数据类型。

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

会抛出'不一致的数据类型错误'

答案 2 :(得分:18)

NVL和COALESCE用于实现在列返回NULL时提供默认值的相同功能。

区别在于:

  1. NVL只接受2个参数,而COALESCE可以接受多个参数 参数
  2. NVL首先评估参数和COALESCE停止 出现非空值。
  3. NVL根据第一个参数执行隐式数据类型转换 给它。 COALESCE希望所有参数都具有相同的数据类型。
  4. COALESCE在使用UNION子句的查询中提供问题。例 以下
  5. COALESCE是ANSI标准,因为NVL是Oracle特定的。
  6. 第三种情况的例子。其他情况很简单。

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

答案 3 :(得分:16)

计划处理也有区别。

当搜索包含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

答案 4 :(得分:4)

其实我不能同意每一个陈述。

&#34; COALESCE希望所有参数都具有相同的数据类型。&#34;

这是错误的,见下文。参数可以是不同的数据类型,也是documented如果所有出现的expr都是数值数据类型或任何可以隐式转换为数字数据类型的非数字数据类型,那么Oracle数据库会确定参数具有最高数字优先级,隐式将剩余参数转换为该数据类型,并返回该数据类型。。实际上,这甚至与普通表达式相矛盾; COALESCE在第一次出现非空值时停止&#34;否则第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!

答案 5 :(得分:4)

另一个证据表明coalesce()不会停止使用第一个非空值进行评估:

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

运行此选项,然后选中my_sequence.currval;

答案 6 :(得分:3)

虽然这个很明显,甚至提到汤姆提出这个问题的方式。但是让我们再说一遍。

NVL只能有2个参数。合并可能超过2个。

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

答案 7 :(得分:2)

NVL:用值替换null。

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

表: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我们可以实现所有三个规则。