ChatGPT解决这个技术问题 Extra ChatGPT

NVL 和 Coalesce 之间的 Oracle 差异

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

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

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

我错过了什么吗?


J
Jon Heller

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
        )

这将运行近 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 不会生成,查询是即时的。


它们并不完全是同义词......至少你可以发现一个不同之处在于,如果给定的值是不同的类型,NVL 会进行隐式数据类型转换。例如,我在使用 COALESCE 传递两个 NULL 值(一个显式设置,另一个取自数据库中的列,类型为 NUMBER)时遇到错误,通过将函数更改为 NVL 就会消失。
G
Gary Myers

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

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

COALESCE 需要一致的数据类型。

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

将抛出“不一致的数据类型错误”


B
Brahmareddy K

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

区别在于:

NVL 只接受 2 个参数,而 COALESCE 可以接受多个参数 NVL 评估两个参数,并且 COALESCE 在第一次出现非 Null 值时停止。 NVL 基于给它的第一个参数进行隐式数据类型转换。 COALESCE 期望所有参数具有相同的数据类型。 COALESCE 在使用 UNION 子句的查询中给出问题。下面的示例 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
      );

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


我不认为“联合”存在特定问题,以至于 Oracle 希望在默认情况下将子查询中的 cast null 类型转换为 char,然后您的项目 3 中列出了相同的问题(混合数据类型)。如果您将其更改为 TO_DATE(NULL) 您可能不会收到错误(我无法在我正在使用的 Oracle 版本上重现该错误)。否则,我同意并感谢您的回答。 :-)
V
Vadzim

计划处理也有区别。

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


K
Kirk Broadhurst

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

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

运行它,然后检查 my_sequence.currval;


是的。确实如此。我创建了 my_sequence 从 1 递增 1 开始,然后运行您的查询,然后选择 my_sequence.currval 并返回 1。
s
sandip

NVL:将空值替换为值。

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


您对 NVL(Purchase_Price + (Purchase_Price * 0.10), nvl(Min_Price,50)) 的评价。或大约:nvl(NVL(Purchase_Price + (Purchase_Price * 0.10), Min_Price) ,50) :)
哪个更快,性能明智应该使用什么?考虑加载数千条记录?
W
Wernfried Domscheit

其实我不能同意每一个说法。

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

这是错误的,见下文。参数可以是不同的数据类型,也可以是 documented如果所有出现的 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;
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!

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

虽然这个很明显,甚至在提出这个问题的汤姆提出的一种方式中也提到过。但是让我们再放一次。

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

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