Sự khác biệt của Oracle giữa NVL và Coalesce


207

Có sự khác biệt rõ ràng giữa NVL và Coalesce trong Oracle không?

Sự khác biệt rõ ràng là sự kết hợp sẽ trả về mục không null đầu tiên trong danh sách tham số của nó trong khi nvl chỉ nhận hai tham số và trả về tham số đầu tiên nếu nó không null, nếu không, nó sẽ trả về mục thứ hai.

Có vẻ như NVL có thể chỉ là một phiên bản 'Case Case "hợp nhất.

Tui bỏ lỡ điều gì vậy?


Câu trả lời:


311

COALESCElà chức năng hiện đại hơn là một phần của ANSI-92tiêu chuẩn.

NVLOraclecụ thể, nó đã được giới thiệu trong 80trước khi có bất kỳ tiêu chuẩn nào.

Trong trường hợp có hai giá trị, chúng là từ đồng nghĩa.

Tuy nhiên, chúng được thực hiện khác nhau.

NVLluôn luôn đánh giá cả hai đối số, trong khi COALESCEthường dừng đánh giá bất cứ khi nào nó tìm thấy cái không đầu tiên NULL(có một số ngoại lệ, chẳng hạn như chuỗi NEXTVAL):

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

Điều này diễn ra trong gần như 0.5vài giây, vì nó tạo ra SYS_GUID(), mặc dù 1không phải là a NULL.

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

Điều này hiểu rằng đó 1không phải là một NULLvà không đánh giá đối số thứ hai.

SYS_GUIDKhông được tạo và truy vấn là ngay lập tức.


11
Chúng không phải là từ đồng nghĩa chính xác ... Ít nhất bạn có thể tìm thấy một sự khác biệt trong thực tế là NVL tạo ra một kiểu truyền dữ liệu ẩn nếu các giá trị đã cho là các loại khác nhau. Vì vậy, ví dụ, tôi đã gặp lỗi khi sử dụng COALESCE truyền cho nó hai giá trị NULL (một giá trị được đặt rõ ràng và một giá trị được lấy từ một cột trong cơ sở dữ liệu, loại SỐ), chỉ biến mất bằng cách thay đổi hàm thành NVL.
DanielM 3/03/2015

170

NVL sẽ thực hiện chuyển đổi ngầm định thành kiểu dữ liệu của tham số đầu tiên, do đó, sau đây không có lỗi

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

COALESCE mong đợi các kiểu dữ liệu nhất quán.

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

sẽ đưa ra một 'lỗi kiểu dữ liệu không nhất quán'


22

NVL và COALESCE được sử dụng để đạt được cùng chức năng cung cấp giá trị mặc định trong trường hợp cột trả về NULL.

Sự khác biệt là:

  1. NVL chỉ chấp nhận 2 đối số trong khi COALESCE có thể nhận nhiều đối số
  2. NVL đánh giá cả các đối số và COALESCE dừng ở lần xuất hiện đầu tiên của giá trị không Null.
  3. NVL thực hiện chuyển đổi kiểu dữ liệu ngầm dựa trên đối số đầu tiên được cung cấp cho nó. COALESCE hy vọng tất cả các đối số sẽ có cùng kiểu dữ liệu.
  4. COALESCE đưa ra các vấn đề trong các truy vấn sử dụng mệnh đề UNION. Ví dụ dưới đây
  5. COALESCE là tiêu chuẩn ANSI trong đó NVL là đặc thù của Oracle.

Ví dụ cho trường hợp thứ ba. Các trường hợp khác là đơn giản.

select nvl('abc',10) from dual; sẽ hoạt động vì NVL sẽ thực hiện chuyển đổi ngầm định số 10 thành chuỗi.

select coalesce('abc',10) from dual; sẽ thất bại với Lỗi - kiểu dữ liệu không nhất quán: CHAR dự kiến ​​đã có SỐ

Ví dụ cho trường hợp sử dụng UNION

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

thất bại với 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
      ) ;

thành công

Thêm thông tin: http://www.plsqlinatures.com/2016/04/difference-b between-nll- and- coalesce-in-oracle.html


Tôi không nghĩ rằng có một vấn đề cụ thể với "union" nên có vẻ như Oracle muốn nhập cast null trong truy vấn phụ của bạn theo char theo mặc định và sau đó bạn có cùng một vấn đề được liệt kê trong mục 3 của bạn (dữ liệu hỗn hợp các loại). Nếu bạn đổi nó thành TO_DATE (NULL), bạn có thể sẽ không gặp lỗi (Tôi không thể tái tạo lỗi trên phiên bản Oracle tôi đang sử dụng). Nếu không, tôi đồng ý và đánh giá cao câu trả lời của bạn. :-)
giật gân

17

Cũng có sự khác biệt trong xử lý kế hoạch.

Oracle có thể hình thành một kế hoạch tối ưu hóa với việc ghép các bộ lọc nhánh khi tìm kiếm chứa so sánh nvlkết quả với một cột được lập chỉ mục.

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)

hợp nhất:

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

Tín dụng truy cập http://www.xt-r.com/2012/03/nvl-coalesce-concatenation.html .


6

Một bằng chứng khác cho thấy hợp nhất () không dừng đánh giá với giá trị không null đầu tiên:

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

Chạy cái này, rồi kiểm tra my_sequence.currval;


5

Thật ra tôi không thể đồng ý với từng tuyên bố.

"COALESCE hy vọng tất cả các đối số sẽ có cùng kiểu dữ liệu."

Điều này là sai, xem bên dưới. Các đối số có thể là các loại dữ liệu khác nhau, cũng được ghi lại : Nếu tất cả các lần xuất hiện của expr là loại dữ liệu số hoặc bất kỳ loại dữ liệu không có số liệu nào có thể được chuyển đổi thành loại dữ liệu số, thì Cơ sở dữ liệu Oracle sẽ xác định đối số có mức ưu tiên số cao nhất, mặc nhiên chuyển đổi các đối số còn lại thành kiểu dữ liệu đó và trả về kiểu dữ liệu đó. . Trên thực tế, điều này thậm chí còn mâu thuẫn với biểu thức chung "COALESCE dừng ở lần xuất hiện đầu tiên của giá trị không Null", nếu không, trường hợp thử nghiệm số 4 không nên gây ra lỗi.

Cũng theo trường hợp thử nghiệm số 5 COALESCE, một chuyển đổi ngầm định của các đối số.

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!

1
Re: Test 4 mâu thuẫn "COALESCE dừng đánh giá ở giá trị khác không đầu tiên" . Tôi không đồng ý. Kiểm tra 4 cho thấy trình biên dịch kiểm tra tính nhất quán của kiểu dữ liệu với COALESCE. Dừng lại ở giá trị khác null đầu tiên là vấn đề thời gian chạy, không phải là vấn đề thời gian biên dịch. Tại thời điểm biên dịch, trình biên dịch không biết rằng giá trị thứ ba (nói) sẽ không có giá trị; nó khẳng định rằng đối số thứ tư cũng thuộc loại dữ liệu phù hợp, ngay cả khi giá trị thứ tư đó sẽ không bao giờ thực sự được đánh giá.
mathguy

3

Mặc dù điều này là rõ ràng, và thậm chí được đề cập theo cách đưa ra bởi Tom, người đã hỏi câu hỏi này. Nhưng hãy đưa lên một lần nữa.

NVL chỉ có thể có 2 đối số. Hợp nhất có thể có nhiều hơn 2.

select nvl('','',1) from dual;// Kết quả :: ORA-00909số lượng đối số không hợp lệ
select coalesce('','','1') from dual; // Kết quả: trả về 1


3

NVL: Thay thế null bằng giá trị.

COALESCE: Trả về biểu thức không null đầu tiên từ danh sách biểu thức.

Bảng: GIÁE_LIST

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

Dưới đây là ví dụ về

[1] Đặt giá bán với thêm 10% lợi nhuận cho tất cả các sản phẩm.
[2] Nếu không có giá niêm yết mua thì giá bán là giá tối thiểu. Để bán giải phóng mặt bằng.
[3] Nếu cũng không có giá tối thiểu, thì hãy đặt giá bán làm giá mặc định "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

Giải thích với ví dụ thực tế cuộc sống.

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

Bạn có thể thấy rằng với NVL, chúng ta có thể đạt được các quy tắc [1], [2]
Nhưng với COALSECE, chúng ta có thể đạt được cả ba quy tắc.


những gì bạn nói về NVL(Purchase_Price + (Purchase_Price * 0.10), nvl(Min_Price,50)) . Hoặc về: nvl(NVL(Purchase_Price + (Purchase_Price * 0.10), Min_Price) ,50) :)
Florin Ghita

Cái nào nhanh hơn, hiệu suất khôn ngoan nên dùng cái gì? xem xét hàng ngàn hồ sơ để tải?
rickyProgrammer
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.