NVLとCoalesceのOracleの違い


207

OracleのNVLとCoalesceの間に明らかな違いはありますか?

明らかな違いは、coalesceはパラメーターリストの最初のnull以外の項目を返すのに対し、nvlは2つのパラメーターのみを受け取り、nullでない場合は最初のパラメーターを返し、それ以外の場合は2番目のパラメーターを返します。

NVLは合体の「ベースケース」バージョンにすぎないようです。

何か不足していますか?


回答:


311

COALESCEANSI-92標準の一部であるより近代的な機能です。

NVLOracle特定のもので80あり、標準が存在する前にで導入されました。

2つの値の場合、それらは同義語です。

ただし、実装方法は異なります。

NVLは常に両方の引数を評価しますが、COALESCE通常は最初の非を見つけると評価を停止しますNULL(sequenceなどのいくつかの例外がありますNEXTVAL):

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

0.5生成されないためSYS_GUID()、これはほぼ数秒間実行されます。1NULL

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

これ1はがでないことを理解しNULL、2番目の引数を評価しません。

SYS_GUIDは生成されず、クエリは瞬時に行われます。


11
それらは厳密には同義語ではありません...少なくとも、与えられた値が異なる型である場合、NVLが暗黙的なデータ型キャストを行うという事実に違いを見つけることができます。たとえば、COALESCEを使用して2つのNULL値(1つは明示的に設定され、もう1つはデータベースの列から取得されたNUMBERタイプ)を渡すとエラーが発生し、関数をNVLに変更すると消えます。
DanielM 2015

170

NVLは暗黙的に最初のパラメーターのデータ型に変換するので、以下はエラーになりません

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

COALESCEは一貫したデータ型を期待しています。

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

「一貫性のないデータ型エラー」をスローします


22

NVLおよびCOALESCEは、列がNULLを返す場合にデフォルト値を提供する同じ機能を実現するために使用されます。

違いは次のとおりです。

  1. NVLは2つの引数しか受け入れませんが、COALESCEは複数の引数を取ることができます
  2. NVLは引数の両方を評価し、COALESCEは非null値が最初に発生したときに停止します。
  3. NVLは、指定された最初の引数に基づいて暗黙的なデータ型変換を行います。COALESCEは、すべての引数が同じデータ型であることを期待します。
  4. COALESCEは、UNION句を使用するクエリで問題を発生させます。以下の例
  5. COALESCEはANSI標準であり、NVLはOracle固有です。

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


「ユニオン」に特定の問題があるとは思わないので、Oracleはデフォルトでサブクエリでnullをcharに型キャストしたいので、アイテム3(混合データ)に同じ問題がリストされているようです。タイプ)。これをTO_DATE(NULL)に変更すると、おそらくエラーは発生しません(使用しているOracleのバージョンではエラーを再現できません)。そうでなければ、私はあなたの答えに同意し、感謝します。:-)
スプラッシュアウト

17

プランの扱いにも違いがあります。

Oracleは、検索にnvlインデックス付き列との結果の比較が含まれている場合に、ブランチフィルターを連結して最適化された計画を形成できます。

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に移動します


6

coalesce()が最初のnull以外の値で評価を停止しないことの別の証明:

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

これを実行してから確認してください my_sequence.currval;


5

実際、私はそれぞれの意見に同意することはできません。

「COALESCEは、すべての引数が同じデータ型であることを期待しています。」

これは間違っています。以下を参照してください。引数はさまざまなデータ型にすることができます。これドキュメント化されています。exprのすべての出現が数値データ型または数値データ型に暗黙的に変換できる非数値データ型である場合、Oracle Databaseは数値の優先順位が最も高い引数を暗黙的に決定します残りの引数をそのデータ型に変換し、そのデータ型を返します。。実際、これは一般的な式「COALESCEは最初に非null値が発生すると停止する」と矛盾します。それ以外の場合、テストケース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!

1
Re:テスト4は、「COALESCEは最初のnull以外の値で評価を停止する」と矛盾します。同意しません。テスト4は、コンパイラーがCOALESCEとのデータ型の整合性をチェックすることを示しています。最初のnull以外の値で停止するのはランタイムの問題であり、コンパイル時の問題ではありません。コンパイル時に、コンパイラーは3番目の値(たとえば)が非ヌルになることを認識していません。4番目の値が実際には評価されない場合でも、4番目の引数も正しいデータ型であると主張します。
mathguy 2016

3

これは明白ですが、この質問をしたトムが立てた方法でさえ言及されました。しかし、もう一度我慢しましょう。

NVLは2つの引数しか持てません。結合には2つ以上ある場合があります。

select nvl('','',1) from dual;//結果::ORA-00909引数の数が無効です
select coalesce('','','1') from dual; //出力:1を返します


3

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つのルールすべてを達成できます。


あなたが言うことNVL(Purchase_Price + (Purchase_Price * 0.10), nvl(Min_Price,50)) 。または:nvl(NVL(Purchase_Price + (Purchase_Price * 0.10), Min_Price) ,50) :)
Florin Ghita

どちらがより速く、パフォーマンスが賢明ですか?ロードする数千のレコードを検討していますか?
rickyProgrammer 2018
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.