【Oracle SQL】NVL/NVL2関数でNULLを置換!計算誤差を防ぐ

Oracle Master Silver

Oracle Databaseにおいて、SQLでの集計や計算結果が合わない最大の原因は「NULL」の扱いにあります。OracleではNULLを含む算術演算は結果がすべてNULLになってしまうため、NVLやNVL2関数を用いた適切なハンドリングが必須です。
本記事では、実機で動くサンプルデータを使い、NULL置換の基本から計算式での活用法、よくあるエラーの回避策までを解説します。

💰 【PR】Oracleエンジニアの市場価値、調べてみませんか?

Oracleのスキルは需要が高く、特定の資格や経験を持っていると年収が大幅にアップするケースがあります。まずはIT専門のエージェントで非公開求人をチェックしてみませんか?


結論・速習:NVLとNVL2の使い分け

NULL対策は、状況に応じて以下の関数を使い分けます。

  • NVL関数:単純な置換を行う(基本)
    • 構文:NVL( 対象列, NULL時の置換値 )
    • 用途:NULLなら「0」や「該当なし」に変換する。
  • NVL2関数:NULLか否かで処理を分岐する(応用)
    • 構文:NVL2( 対象列, 非NULL時の戻り値, NULL時の戻り値 )
    • 用途:値がある時は「あり」、ない時は「なし」と表示するなど。
  • 注意点:置換する値のデータ型は、対象列と一致(または暗黙変換可能)させる必要があります。

1. NULL値と算術式の基礎知識

NULLの伝播(Propagate)とは

Oracle DatabaseにおけるNULLは「値が存在しない状態(未知)」を指します。そのため、100 + NULL のような計算を行うと、結果は 100 ではなく NULL になります。これを「NULLの伝播」と呼びます。

SQL> set null '(null)'
SQL> select 100+NULL from dual;

100+NULL
----------
(null)

この特性により、売上計算や給与計算で一部のカラムがNULLの場合、合計値が消えてしまう事故が発生します。これを防ぐのが NVL 関数です。


2. 実践:サンプルデータの作成

解説用のテーブル EMPLOYEE_DATA を作成します。
※お手元の検証環境(Oracle 19c等)でそのまま実行可能です。

前提条件

  • 接続ユーザーに対象スキーマへの CREATE TABLE, UNLIMITED TABLESPACE 権限があること。
-- 既存テーブルがある場合は削除(実環境では注意)
-- DROP TABLE EMPLOYEE_DATA PURGE;

-- 1. テーブル作成
CREATE TABLE EMPLOYEE_DATA (
    EMP_ID   NUMBER PRIMARY KEY, -- 社員ID
    EMP_NAME VARCHAR2(50),       -- 社員名
    SALARY   NUMBER,             -- 給与
    BONUS    NUMBER              -- ボーナス(NULL許容)
);

-- 2. データ挿入(NULLを含むパターン)
INSERT INTO EMPLOYEE_DATA VALUES (1, 'Alice',   5000, 300);  -- 両方あり
INSERT INTO EMPLOYEE_DATA VALUES (2, 'Bob',     NULL, 200);  -- 給与がNULL
INSERT INTO EMPLOYEE_DATA VALUES (3, 'Charlie', 4000, NULL); -- ボーナスがNULL
INSERT INTO EMPLOYEE_DATA VALUES (4, 'Diana',   NULL, NULL); -- 両方NULL
INSERT INTO EMPLOYEE_DATA VALUES (5, 'Eve',     6000, 400);
INSERT INTO EMPLOYEE_DATA VALUES (6, 'Frank',   NULL, 100);

-- 3. コミット
COMMIT;

-- 4. 現状の確認
SELECT * FROM EMPLOYEE_DATA ORDER BY EMP_ID;
SQL> set null '(null)'
SQL> SELECT * FROM EMPLOYEE_DATA ORDER BY EMP_ID;

EMP_ID EMP_NAME SALARY BONUS
---------- -------------------- ---------- ----------
1 Alice 5000 300
2 Bob (null) 200
3 Charlie 4000 (null)
4 Diana (null) (null)
5 Eve 6000 400
6 Frank (null) 100

6 rows selected.

3. NVL関数による計算誤差の回避

まずは、NULLをケアせずに計算した場合と、NVLを使用した場合を比較します。

実行例:給与+ボーナスの合計算出

SELECT 
    EMP_ID, 
    EMP_NAME, 
    -- [NG例] そのまま足すと、どちらかがNULLの場合に結果が消える
    SALARY + BONUS AS TOTAL_NG,
    -- [OK例] NVLでNULLを0に変換してから足す
    NVL(SALARY, 0) + NVL(BONUS, 0) AS TOTAL_OK
FROM EMPLOYEE_DATA
ORDER BY EMP_ID;
SQL> set null '(null)'
SQL> SELECT
2 EMP_ID,
3 EMP_NAME,
4 SALARY + BONUS AS TOTAL_NG,
5 NVL(SALARY, 0) + NVL(BONUS, 0) AS TOTAL_OK
6 FROM EMPLOYEE_DATA
7 ORDER BY EMP_ID;

EMP_ID EMP_NAME TOTAL_NG TOTAL_OK
---------- -------------------- ---------- ----------
1 Alice 5300 5300
2 Bob (null) 200
3 Charlie (null) 4000
4 Diana (null) 0
5 Eve 6400 6400
6 Frank (null) 100

6 rows selected.

実行結果と解説

EMP_ID EMP_NAME   TOTAL_NG   TOTAL_OK
------ ---------- ---------- ----------
     1 Alice            5300       5300  <- 両方値があるので一致
     2 Bob            (null)        200  <- NULL+200=NULL(NG) / 0+200=200(OK)
     3 Charlie        (null)       4000  <- 4000+NULL=NULL(NG) / 4000+0=4000(OK)
     4 Diana          (null)          0  <- 両方NULLでも0になる
...
  • 解説: NVL(BONUS, 0) は、「BONUSがNULLなら0を、そうでなければBONUSの値を返す」という処理を行います。これにより算術演算が成立します。

💰 【PR】Oracleエンジニアの市場価値、調べてみませんか?

Oracleのスキルは需要が高く、特定の資格や経験を持っていると年収が大幅にアップするケースがあります。まずはIT専門のエージェントで非公開求人をチェックしてみませんか?


4. NVL2関数による条件分岐

NVL2は「値がある場合」と「NULLの場合」で全く異なる値を返したい時に便利です。

実行例:ボーナス有無に応じた手当の計算

  • ボーナスがある人:ボーナス額 + 500(特別加算)
  • ボーナスがない人:一律 0
SELECT 
    EMP_ID, 
    EMP_NAME, 
    BONUS,
    -- 第1引数(BONUS)がNULLでなければ第2引数、NULLなら第3引数を返す
    NVL2(BONUS, BONUS + 500, 0) AS ADJUSTED_BONUS
FROM EMPLOYEE_DATA
ORDER BY EMP_ID;
SQL> set null '(null)'
SQL> SELECT
2 EMP_ID,
3 EMP_NAME,
4 BONUS,
5 NVL2(BONUS, BONUS + 500, 0) AS ADJUSTED_BONUS
6 FROM EMPLOYEE_DATA
7 ORDER BY EMP_ID;

EMP_ID EMP_NAME BONUS ADJUSTED_BONUS
---------- -------------------- ---------- --------------
1 Alice 300 800
2 Bob 200 700
3 Charlie (null) 0
4 Diana (null) 0
5 Eve 400 900
6 Frank 100 600

6 rows selected.

実行結果

EMP_ID EMP_NAME    BONUS ADJUSTED_BONUS
------ ---------- ------ --------------
     1 Alice         300            800  <- 300 + 500
     2 Bob           200            700  <- 200 + 500
     3 Charlie    (null)              0  <- NULLなので 0
...
  • ポイント: DECODECASE 文でも記述可能ですが、NULL判定に特化しているため NVL2 の方がシンプルに記述できます。

5. トラブルシューティング(よくあるエラー)

NVL/NVL2使用時によく遭遇するエラーと対処法です。

エラーコード原因対処法
ORA-01722: 数値が無効です数値型の列の置換値に、変換できない文字列(’なし’等)を指定した。置換値を数値にするか、対象列を TO_CHAR で文字列化する。
データ型の不一致第1引数と第2引数の型が異なる(例: 日付型列の置換に数値0を指定)。型を合わせる。日付型なら SYSDATE や日付リテラルを使う。

エラーになる例(型不一致)

-- SALARYはNUMBER型なのに、置換値に文字列を指定している
SELECT NVL(SALARY, '未払い') FROM EMPLOYEE_DATA;
-- 結果: ORA-01722: 数値が無効です

修正案: 表示目的であれば、左側を文字列に変換します。

SELECT NVL(TO_CHAR(SALARY), '未払い') FROM EMPLOYEE_DATA;

6. 運用・実装上の注意点とベストプラクティス

メリットとデメリット

  • メリット: SQLが簡潔になり、アプリケーション側でのNULLチェック処理を減らせる。
  • デメリット: 独自関数であるため、将来的に他のDBMS(PostgreSQLやMySQLなど)へ移行する際に修正が必要になる場合があります。

COALESCE関数との違い(重要)

標準SQL(ANSI)準拠の COALESCE 関数も似た動きをしますが、以下の違いがあります。

  1. 引数の数: NVLは2つまで。COALESCEは複数指定可能(左から順に最初の非NULLを返す)。
  2. 評価プロセス(パフォーマンス):
    • NVL: 第1引数がNULLでなくても、第2引数を評価(計算)します
    • COALESCE: 第1引数が非NULLなら、第2引数は評価しません(ショートサーキット)
    • ※第2引数に重い関数処理を入れる場合は COALESCE の方が高速な場合があります。

テーブル設計での回避(推奨)

SQLで都度 NVL するよりも、テーブル設計時点で DEFAULT 0 制約を付け、NOT NULL 制約を課す方が、データの整合性とパフォーマンスの観点で望ましいです。

-- 推奨されるテーブル定義例
CREATE TABLE NEW_EMP (
    SALARY NUMBER DEFAULT 0 NOT NULL,
    BONUS  NUMBER DEFAULT 0 NOT NULL
);

7. FAQ:よくある質問

Q1. NVLの中でNVLを使うことはできますか?(ネスト)

A. 可能です。しかし、複数の候補値を順にチェックしたい場合は COALESCE(列A, 列B, 列C, 0) を使う方が可読性が高く、標準的です。

Q2. 空文字(”)はNVLで置換できますか?

A. はい。Oracle Databaseでは「空文字(長さ0の文字列)」は NULL と同等に扱われる仕様のため、NVL('', '置換') は「置換」を返します(他DBでは挙動が異なるので注意)。

Q3. WHERE句でNVLを使うと遅くなりますか?

A. インデックスが効かなくなる可能性があります。WHERE NVL(COL, 0) = 0 とすると、COL列のインデックスが使用されません。これを避けるには、ファンクション索引を作成するか、条件式を WHERE COL IS NULL OR COL = 0 のように工夫する必要があります。


8. まとめ

  • NVL はNULLを特定の値(0や文字列)に変換し、計算誤差や表示欠けを防ぐ基本関数。
  • NVL2 は「ある時」「ない時」の分岐ロジックをシンプルに書ける。
  • 型合わせ は必須。数値項目に文字列を置換しようとするとエラーになる。
  • 標準SQL を意識するなら COALESCE の利用も検討する。

NULLハンドリングは正確なデータ集計の第一歩です。まずは NVL(列, 0) のパターンからマスターしましょう。

💰 【PR】Oracleエンジニアの市場価値、調べてみませんか?

Oracleのスキルは需要が高く、特定の資格や経験を持っていると年収が大幅にアップするケースがあります。まずはIT専門のエージェントで非公開求人をチェックしてみませんか?

コメント

タイトルとURLをコピーしました