OracleのNVLとCoalesceの間に明らかな違いはありますか?
明らかな違いは、colesceがパラメータリストの最初の非nullアイテムを返すのに対し、nvlは2つのパラメータのみを取り、nullでない場合は最初のパラメータを返します。それ以外の場合は2番目のパラメータを返します。
NVLは合体の「ベースケース」バージョンにすぎないようです。
私は何かが足りないのですか?
COALESCE
ANSI-92
標準の一部であるより現代的な機能です。
NVL
Oracle
具体的には、標準が存在する前ので導入されました80
。
2つの値の場合、それらは同義語です。
ただし、実装方法は異なります。
NVL
常に両方の引数を評価しますが、COALESCE
通常、最初の非引数が見つかると評価を停止しますNULL
(シーケンスなどのいくつかの例外がありますNEXTVAL
)。
SELECT SUM(val)
FROM (
SELECT NVL(1, LENGTH(RAWTOHEX(SYS_GUID()))) AS val
FROM dual
CONNECT BY
level <= 10000
)
これは、ではなくても'sを生成するため、ほぼ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
これは、それがaではないことを理解しNULL
、2番目の引数を評価しません。
SYS_GUID
は生成されず、クエリは即座に実行されます。
NVLは最初のパラメーターのデータ型に暗黙的に変換するため、以下のエラーは発生しません。
select nvl('a',sysdate) from dual;
COALESCEは、一貫したデータ型を期待しています。
select coalesce('a',sysdate) from dual;
'一貫性のないデータ型エラー'をスローします
NVLとCOALESCEは、列がNULLを返した場合にデフォルト値を提供するという同じ機能を実現するために使用されます。
違いは次のとおりです。
3番目のケースの例。他の場合は単純です。
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
プランの取り扱いにも違いがあります。
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に移動します。
Coalesce()が最初のnull以外の値で評価を停止しないという別の証拠:
SELECT COALESCE(1, my_sequence.nextval) AS answer FROM dual;
これを実行してから、my_sequence.currval;
NVL: nullを値に置き換えます。
COALESCE:式リストから最初のnull以外の式を返します。
テーブル: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を使用すると、3つのルールすべてを達成できます。
実際、私はそれぞれの声明に同意することはできません。
「COALESCEは、すべての引数が同じデータ型であることを期待しています。」
これは間違っています。以下を参照してください。引数は異なるデータ型にすることができ、これも文書化されています。exprのすべてのオカレンスが数値データ型、または暗黙的に数値データ型に変換できる非数値データ型である場合、Oracle Databaseは、数値の優先順位が最も高い引数を暗黙的に決定します。残りの引数をそのデータ型に変換し、そのデータ型を返します。。実際、これは一般的な表現「COALESCEはNull以外の値の最初の出現で停止する」と矛盾します。そうでない場合、テストケースNo.4でエラーが発生することはありません。
また、テストケースNo. 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!
これは明らかですが、この質問をしたトムが立てた方法でさえ言及されています。しかし、もう一度我慢しましょう。
NVLは2つの引数しか持てません。Coalesceには2つ以上ある場合があります。
select nvl('','',1) from dual;
//結果::ORA-00909
引数の数が無効です
select coalesce('','','1') from dual;
//出力:1を返します