Perbedaan Oracle antara NVL dan Coalesce

208

Apakah ada perbedaan yang tidak jelas antara NVL dan Coalesce di Oracle?

Perbedaan yang jelas adalah bahwa penggabungan akan mengembalikan item bukan nol pertama dalam daftar parameternya sedangkan nvl hanya mengambil dua parameter dan mengembalikan yang pertama jika bukan nol, jika tidak mengembalikan yang kedua.

Tampaknya NVL mungkin hanya versi 'Base Case "penggabungan.

Apakah saya melewatkan sesuatu?

Tom Hubbard
sumber
Lebih lanjut di sini: jonathanlewis.wordpress.com/2018/02/13/coalesce-v-nvl
William Robertson

Jawaban:

312

COALESCE adalah fungsi yang lebih modern yang merupakan bagian dari ANSI-92 standar.

NVLadalah Oracletertentu, itu diperkenalkan di80 's sebelum ada standar apapun.

Dalam hal dua nilai, mereka adalah sinonim.

Namun, mereka diimplementasikan secara berbeda.

NVLselalu mengevaluasi kedua argumen, sementara COALESCEbiasanya berhenti evaluasi setiap kali ia menemukan non- pertama NULL(ada beberapa pengecualian, seperti urutan NEXTVAL):

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

Ini berjalan selama hampir 0.5detik, karena itu menghasilkan SYS_GUID(), meskipun 1bukan NULL.

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

Ini mengerti bahwa 1itu bukan a NULLdan tidak mengevaluasi argumen kedua.

SYS_GUIDTidak dibuat dan permintaannya instan.

Quassnoi
sumber
11
Mereka bukan sinonim ... Setidaknya Anda dapat menemukan perbedaan dalam fakta bahwa NVL membuat casting tipe data implisit jika nilai yang diberikan adalah tipe yang berbeda. Jadi misalnya, saya mendapatkan kesalahan menggunakan COALESCE dengan mengirimkannya dua nilai NULL (satu set secara eksplisit dan yang lainnya diambil dari kolom dalam database, tipe NUMBER), yang hilang begitu saja dengan mengubah fungsi menjadi NVL.
DanielM
170

NVL akan melakukan konversi implisit ke tipe data dari parameter pertama, jadi yang berikut ini tidak salah

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

COALESCE mengharapkan tipe data yang konsisten.

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

akan melempar 'kesalahan datatype tidak konsisten'

Gary Myers
sumber
22

NVL dan COALESCE digunakan untuk mencapai fungsionalitas yang sama dengan memberikan nilai default jika kolom mengembalikan NULL.

Perbedaannya adalah:

  1. NVL hanya menerima 2 argumen sedangkan COALESCE dapat mengambil beberapa argumen
  2. NVL mengevaluasi argumen dan COALESCE berhenti pada kemunculan pertama dari nilai non-Null.
  3. NVL melakukan konversi tipe data implisit berdasarkan argumen pertama yang diberikan kepadanya. COALESCE mengharapkan semua argumen memiliki tipe data yang sama.
  4. COALESCE memberikan masalah dalam kueri yang menggunakan klausa UNION. Contoh di bawah ini
  5. COALESCE adalah standar ANSI sedangkan NVL adalah Oracle spesifik.

Contoh untuk kasus ketiga. Kasus-kasus lain sederhana.

select nvl('abc',10) from dual; akan berfungsi karena NVL akan melakukan konversi implisit dari angka 10 ke string.

select coalesce('abc',10) from dual; akan gagal dengan Kesalahan - tipe data tidak konsisten: diharapkan CHAR mendapat NUMBER

Contoh untuk UNION use-case

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

gagal dengan 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
      ) ;

berhasil.

Informasi lebih lanjut: http://www.plsqlinformation.com/2016/04/difference-between-nvl-and-coalesce-in-oracle.html

Brahmareddy K
sumber
Saya tidak berpikir bahwa ada masalah khusus dengan "union" sehingga Oracle ingin mengetikkan null di dalam Anda sub-query ke char secara default dan kemudian Anda memiliki masalah yang sama tercantum dalam item 3 Anda (data campuran jenis). Jika Anda mengubahnya menjadi TO_DATE (NULL) Anda mungkin tidak akan mendapatkan kesalahan (Saya tidak dapat mereproduksi kesalahan pada versi Oracle yang saya gunakan). Kalau tidak, saya setuju dan menghargai jawaban Anda. :-)
splashout
17

Ada juga perbedaan dalam penanganan rencana.

Oracle dapat membentuk rencana yang dioptimalkan dengan rangkaian filter cabang saat pencarian berisi perbandingan nvlhasil dengan kolom yang diindeks.

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)

bersatu:

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

Kredit masuk ke http://www.xt-r.com/2012/03/nvl-coalesce-concatenation.html .

Vadzim
sumber
6

Bukti lain yang menyatu () tidak menghentikan evaluasi dengan nilai non-nol pertama:

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

Jalankan ini, lalu periksa my_sequence.currval;

Ramuan Swift
sumber
5

Sebenarnya saya tidak bisa menyetujui setiap pernyataan.

"COALESCE mengharapkan semua argumen memiliki tipe data yang sama."

Ini salah, lihat di bawah. Argumen dapat berupa tipe data yang berbeda, yang juga didokumentasikan : Jika semua kejadian expr adalah tipe data numerik atau tipe data nonnumerik apa pun yang dapat secara implisit dikonversi ke tipe data numerik, maka Oracle Database menentukan argumen dengan prioritas numerik tertinggi, secara implisit mengonversi argumen yang tersisa ke tipe data itu, dan mengembalikan tipe data itu.. Sebenarnya ini bahkan bertentangan dengan ekspresi umum "COALESCE berhenti pada kemunculan pertama dari nilai non-Null", jika uji kasus No. 4 tidak boleh menimbulkan kesalahan.

Juga menurut uji kasus No. 5 COALESCEmelakukan konversi argumen secara implisit.

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!
Wernfried Domscheit
sumber
1
Re: Uji 4 bertentangan "COALESCE menghentikan evaluasi pada nilai bukan nol pertama" . Saya tidak setuju. Uji 4 menunjukkan bahwa kompiler memeriksa konsistensi tipe data dengan COALESCE. Berhenti pada nilai non-nol pertama adalah masalah runtime, bukan masalah waktu kompilasi. Pada waktu kompilasi, kompiler tidak tahu bahwa nilai ketiga (katakanlah) akan non-nol; itu menegaskan bahwa argumen keempat juga dari tipe data yang benar, bahkan jika nilai keempat itu tidak akan pernah benar-benar dievaluasi.
mathguy
3

Padahal yang ini sudah jelas, dan bahkan disebutkan dengan cara disiapkan oleh Tom yang menanyakan pertanyaan ini. Tapi mari kita pasang lagi.

NVL hanya dapat memiliki 2 argumen. Coalesce mungkin memiliki lebih dari 2.

select nvl('','',1) from dual;// Hasil:: ORA-00909jumlah argumen tidak valid
select coalesce('','','1') from dual; // Output: mengembalikan 1

Neel
sumber
3

NVL: Ganti null dengan nilai.

COALESCE: Kembalikan ekspresi non-nol pertama dari daftar ekspresi.

Tabel: PRICE_LIST

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

Di bawah ini adalah contoh

[1] Tetapkan harga jual dengan menambahkan laba 10% untuk semua produk.
[2] Jika tidak ada harga daftar pembelian, maka harga jual adalah harga minimum. Untuk penjualan obral.
[3] Jika tidak ada harga minimum juga, maka tetapkan harga jual sebagai harga default "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

Jelaskan dengan contoh praktis kehidupan nyata.

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

Anda dapat melihat bahwa dengan NVL kita dapat mencapai aturan [1], [2]
Tetapi dengan COALSECE kita dapat mencapai ketiga aturan tersebut.

sandip
sumber
apa yang kamu katakan tentang NVL(Purchase_Price + (Purchase_Price * 0.10), nvl(Min_Price,50)) . Atau tentang: nvl(NVL(Purchase_Price + (Purchase_Price * 0.10), Min_Price) ,50) :)
Florin Ghita
mana yang lebih cepat, kinerja bijaksana apa yang harus digunakan? mempertimbangkan ribuan catatan untuk dimuat?
rickyProgrammer