NVL与Coalesce之间的Oracle差异

Tom*_*ard 196 sql oracle coalesce nvl

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

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

似乎NVL可能只是一个'基础案例'版本的合并.

我错过了什么吗?

Qua*_*noi 295

COALESCE是更现代的功能,是ANSI-92标准的一部分.

NVLOracle具体的,它在推出80的前存在任何标准.

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

但是,它们的实现方式不同.

NVL总是计算两个参数,同时COALESCE通常在找到第一个非结束时停止评估NULL(有一些例外,例如序列NEXTVAL):

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

它运行了几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
        )
Run Code Online (Sandbox Code Playgroud)

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

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

  • 它们不完全是同义词......至少你可以发现,如果给定的值是不同类型,NVL会进行隐式数据类型转换.因此,例如,我使用COALESCE传递一个错误,传递两个NULL值(一个显式设置,另一个从数据库中的列中获取,类型为NUMBER),只是通过将函数更改为NVL而消失. (9认同)

Gar*_*ers 166

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

select nvl('a',sysdate) from dual;
Run Code Online (Sandbox Code Playgroud)

COALESCE期望一致的数据类型.

select coalesce('a',sysdate) from dual;
Run Code Online (Sandbox Code Playgroud)

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


Bra*_*y K 19

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

不同之处是:

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

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

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
      );
Run Code Online (Sandbox Code Playgroud)

失败了 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
      ) ;
Run Code Online (Sandbox Code Playgroud)

成功.

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

  • 我不认为“union”有一个具体的问题,以至于 Oracle 想要在默认情况下将子查询中的 null 类型转换为 char,然后您在第 3 项中列出了相同的问题(混合数据类型)。如果将其更改为 TO_DATE(NULL),您可能不会收到错误(我无法在我使用的 Oracle 版本上重现该错误)。否则我同意并感谢你的回答。:-) (2认同)

Vad*_*zim 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;
Run Code Online (Sandbox Code Playgroud)

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)
Run Code Online (Sandbox Code Playgroud)

合并:

---------------------------------------------------------------------------------------
| 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))
Run Code Online (Sandbox Code Playgroud)

积分转到http://www.xt-r.com/2012/03/nvl-coalesce-concatenation.html.


小智 6

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

SELECT COALESCE(1, my_sequence.nextval) AS answer FROM dual;
Run Code Online (Sandbox Code Playgroud)

运行这个,然后检查 my_sequence.currval;


san*_*dip 5

NVL:用值替换 null。

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

表:PRICE_LIST

+----------------+-----------+
| Purchase_Price | Min_Price |
+----------------+-----------+
| 10             | null      |
| 20             |           |
| 50             | 30        |
| 100            | 80        |
| null           | null      |
+----------------+-----------+   
Run Code Online (Sandbox Code Playgroud)



下面是[1]设定销售价格,给所有产品加10%利润的例子。
[2] 如果没有购买标价,则销售价格为最低价格。清仓出售。
[3] 如果也没有最低价格,则将销售价格设置为默认价格“50”。

+----------------+-----------+
| Purchase_Price | Min_Price |
+----------------+-----------+
| 10             | null      |
| 20             |           |
| 50             | 30        |
| 100            | 80        |
| null           | null      |
+----------------+-----------+   
Run Code Online (Sandbox Code Playgroud)

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

+----------------+-----------+-----------------+----------------------+
| 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 |
+----------------+-----------+-----------------+----------------------+
Run Code Online (Sandbox Code Playgroud)

您可以看到,使用 NVL 我们可以实现规则 [1]、[2]
,但是使用 COALSECE 我们可以实现所有三个规则。


Wer*_*eit 5

实际上,我不同意每一个陈述。

“ COALESCE期望所有参数都具有相同的数据类型。”

这是错误的,请参阅下文。参数可以是不同的数据类型,也可以进行记录如果所有出现的expr都是数字数据类型或可以隐式转换为数字数据类型的任何非数字数据类型,则Oracle数据库将隐式确定具有最高数字优先级的参数将其余参数转换为该数据类型,然后返回该数据类型。。实际上,这甚至与通用表达式“ 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;
Run Code Online (Sandbox Code Playgroud)
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;
Run Code Online (Sandbox Code Playgroud)

  • 回复:*测试 4 与“COALESCE 在第一个非空值处停止评估”* 相矛盾。我不同意。测试 4 显示编译器检查数据类型与 COALESCE 的一致性。在第一个非空值处停止是运行时问题,而不是编译时问题。在编译时,编译器不知道第三个值(比如说)是非空的;它坚持认为第四个参数也是正确的数据类型,即使第四个值实际上永远不会被评估。 (2认同)