web-dev-qa-db-ja.com

NVLとCoalesceのOracleの違い

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

明らかな違いは、合体はパラメータリストの最初の非ヌル項目を返すのに対して、nvlは2つのパラメータのみを取り、ヌルでない場合は最初を返し、そうでない場合は2番目を返すことです。

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

何か不足していますか?

188
Tom Hubbard

COALESCEは、ANSI-92標準の一部である最新の関数です。

NVLOracle固有であり、標準が存在する前に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
        )

これは、0.5NULLではないにもかかわらず、SYS_GUID()を生成するため、ほぼ1秒間実行されます。

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

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

SYS_GUIDは生成されず、クエリは即座に実行されます。

286
Quassnoi

NVLは、最初のパラメーターのデータ型への暗黙的な変換を行うため、以下はエラーになりません

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

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

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

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

163
Gary Myers

NVLとCOALESCEを使用して、列がNULLを返す場合にデフォルト値を提供するのと同じ機能を実現します。

違いは次のとおりです。

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

3番目のケースの例。他のケースは簡単です。

NVLは数値の10を文字列に暗黙的に変換するため、select nvl('abc',10) from dual;は機能します。

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

18
Brahmareddy K

計画の処理にも違いがあります。

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

16
Vadzim

実際、各声明に同意することはできません。

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

これは間違っています。以下を参照してください。引数はさまざまなデータ型、つまり ドキュメント化されたになりますデータ型の場合、Oracle Databaseは数値の優先順位が最も高い引数を決定し、残りの引数をそのデータ型に暗黙的に変換し、そのデータ型を返します。。実際には、これは「COALESCEが最初にNull以外の値になったときに停止する」という一般的な表現と矛盾している場合もあります。

また、テストケース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!
4

Coalesce()が最初のNULL以外の値で評価を停止しないという別の証拠:

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

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

4
Herb Swift

ただし、これは明らかであり、この質問をしたトムが提示した方法で言及されていました。しかし、もう一度我慢してみましょう。

NVLに指定できる引数は2つだけです。合体には2を超える場合があります。

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

3
Neel

NVL: nullを値に置き換えます。

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を使用すると、3つのルールすべてを達成できます。

2
sandip