Oracle で NULL を別の値に置き換えるには NVL 関数を使います。しかし Oracle には NVL のほかにも NVL2・COALESCE・NULLIF と計 4 つの NULL 処理関数があり、それぞれに使いどころが異なります。
「NVL と COALESCE はどう違うのか」「NVL で型変換エラーが出た」「SUM で NULL の行が無視されるのを防ぎたい」――こうした疑問は、4 関数の仕様を正確に理解すれば解消できます。
本記事では、NVL の基本から NVL2・COALESCE・NULLIF の使い分け、暗黙型変換の落とし穴、COALESCE の短絡評価によるパフォーマンス差、そして計算式・文字列連結・集計関数での実務パターンまで体系的に解説します。
・NVL 関数の基本構文と動作
・NVL2 関数で「NULL の場合」と「NULL でない場合」を分岐する方法
・COALESCE 関数で複数の候補から最初の非 NULL を取得する方法
・NULLIF 関数で特定の値を NULL に変換する方法
・4 関数の比較表と使い分けの判断基準
・NVL の暗黙型変換の落とし穴
・COALESCE と NVL の短絡評価の違い(パフォーマンス)
・集計関数・計算式・文字列連結での NULL 処理パターン
NVL 関数の基本
-- 構文: NVL(expr1, expr2) -- expr1 が NULL なら expr2 を返す。NULL でなければ expr1 をそのまま返す -- 数値: salary が NULL なら 0 に置き換え SELECT employee_id, NVL(salary, 0) AS salary FROM employees; -- 文字列: middle_name が NULL なら '(なし)' に置き換え SELECT NVL(middle_name, '(なし)') AS middle_name FROM employees; -- 日付: end_date が NULL なら現在日時に置き換え SELECT NVL(end_date, SYSDATE) AS end_date FROM projects;
| expr1 の値 | NVL(expr1, 0) の結果 | 説明 |
|---|---|---|
| 500 | 500 | expr1 が NULL でないのでそのまま返す |
| 0 | 0 | 0 は NULL ではないのでそのまま返す |
| NULL | 0 | expr1 が NULL なので expr2(0)を返す |
NVL は第 2 引数の型を第 1 引数の型に暗黙変換します。型が一致しない場合にエラーが発生することがあります。
例:
NVL(salary, 'なし') → salary が NUMBER 型なので’なし’ を NUMBER に変換しようとして ORA-01722: invalid number になります。型を揃えるか、NVL(TO_CHAR(salary), 'なし') と明示変換してください。NVL2 関数:NULL と非 NULL で異なる値を返す
-- 構文: NVL2(expr1, expr_not_null, expr_null)
-- expr1 が NULL でなければ expr_not_null を返す
-- expr1 が NULL なら expr_null を返す
-- commission_pct が NULL なら '固定給'、そうでなければ '歩合あり'
SELECT employee_id,
NVL2(commission_pct, '歩合あり', '固定給') AS pay_type
FROM employees;
-- bonus が NULL なら 0、そうでなければ bonus * 1.1(10%増し)
SELECT employee_id,
NVL2(bonus, bonus * 1.1, 0) AS adjusted_bonus
FROM employees;
NVL2(expr, 非NULLの場合, NULLの場合) です。NVL とは引数の意味が逆順になりがちなので注意してください。「NVL2 の第 2 引数は NULL でない場合の値」と覚えます。COALESCE 関数:複数の候補から最初の非 NULL を取得
-- 構文: COALESCE(expr1, expr2, expr3, ... , default)
-- 左から順に評価し、最初の非 NULL 値を返す
-- 携帯番号 → 自宅番号 → '連絡先なし' の優先順で取得
SELECT employee_id,
COALESCE(mobile_phone, home_phone, '連絡先なし') AS contact
FROM employees;
-- NVL のネストを COALESCE で置き換え
-- 冗長: NVL(mobile_phone, NVL(home_phone, '連絡先なし'))
-- 簡潔: COALESCE(mobile_phone, home_phone, '連絡先なし')
COALESCE と NVL の違い:短絡評価
COALESCE は短絡評価(ショートサーキット)を行います。最初の非 NULL が見つかった時点で残りの引数は評価しません。一方、NVL は両方の引数を常に評価します。
-- NVL: expr2 が常に評価される(サブクエリも実行される) SELECT NVL(name, (SELECT MAX(name) FROM employees)) FROM dual; -- → name が NULL でなくても、サブクエリは実行される -- COALESCE: 最初の非 NULL で停止(サブクエリは実行されない) SELECT COALESCE(name, (SELECT MAX(name) FROM employees)) FROM dual; -- → name が NULL でなければ、サブクエリは実行されない
第 2 引数にサブクエリや関数呼び出しがある場合、NVL は常にそれを実行するため不要な処理コストが発生します。COALESCE なら第 1 引数が非 NULL の行では第 2 引数を評価しないため高速です。また COALESCE は SQL 標準関数なので、他の RDBMS への移植性も高くなります。
NULLIF 関数:特定の値を NULL に変換する
-- 構文: NULLIF(expr1, expr2) -- expr1 = expr2 なら NULL を返す。異なれば expr1 をそのまま返す -- '(不明)' という文字列を NULL に変換 SELECT NULLIF(department_name, '(不明)') AS dept_name FROM departments; -- 0 を NULL に変換(ゼロ除算の防止に便利) SELECT total_amount / NULLIF(quantity, 0) AS unit_price FROM order_details; -- quantity が 0 なら NULL を返す → ORA-01476(ゼロ除算)を防止
a / NULLIF(b, 0) と書くと、b が 0 のときは a / NULL = NULL になり、ORA-01476(ゼロ除算)エラーを回避できます。CASE 文で書くより圧倒的にシンプルです。NVL / NVL2 / COALESCE / NULLIF 比較表
| 関数 | 引数 | 動作 | SQL 標準 | 短絡評価 |
|---|---|---|---|---|
| NVL(a, b) | 2 個 | a が NULL なら b を返す | × Oracle 独自 | × 両方評価 |
| NVL2(a, b, c) | 3 個 | a が NULL でなければ b、NULL なら c | × Oracle 独自 | × 全引数評価 |
| COALESCE(a, b, c, …) | 2 個以上 | 左から最初の非 NULL を返す | ○ SQL 標準 | ○ 短絡評価 |
| NULLIF(a, b) | 2 個 | a = b なら NULL、異なれば a | ○ SQL 標準 | ― |
・NULL を 1 つの代替値に置換 →
NVL または COALESCE・NULL / 非 NULL で異なる値を返す →
NVL2・複数の候補から最初の非 NULL を選ぶ →
COALESCE・特定の値を NULL に変換(ゼロ除算防止等) →
NULLIF・他 RDBMS への移植性を考慮 →
COALESCE(SQL 標準)他 RDBMS との対応
| RDBMS | NULL 置換関数 | 複数候補からの選択 |
|---|---|---|
| Oracle | NVL(a, b) / NVL2(a, b, c) | COALESCE(a, b, c, …) |
| MySQL | IFNULL(a, b) | COALESCE(a, b, c, …) |
| SQL Server | ISNULL(a, b) | COALESCE(a, b, c, …) |
| PostgreSQL | (COALESCE のみ) | COALESCE(a, b, c, …) |
| SQLite | IFNULL(a, b) | COALESCE(a, b, c, …) |
COALESCE は全 RDBMS で共通に使えるため、移植性を重視する場合は NVL の代わりに COALESCE を使うのが安全です。
集計関数での NVL の使い方
-- SUM は NULL 行を無視する(0 として計算されない) -- commission_pct: 10, NULL, 20, NULL → SUM = 30(NULL を無視) SELECT SUM(commission_pct) FROM employees; -- NULL を 0 として合計したい場合 SELECT SUM(NVL(commission_pct, 0)) FROM employees; -- 10 + 0 + 20 + 0 = 30(結果は同じだが意味が明確) -- AVG で影響が出るケース(NVL の有無で結果が変わる) -- NVL なし: AVG = (10 + 20) / 2 = 15(NULL 行を除外して平均) SELECT AVG(commission_pct) FROM employees; -- NVL あり: AVG = (10 + 0 + 20 + 0) / 4 = 7.5(全行で平均) SELECT AVG(NVL(commission_pct, 0)) FROM employees;
SUM では NVL の有無で結果は変わりませんが、AVG では分母が変わるため結果が大きく異なります。NULL 行を「値なし(除外)」として扱うのか「0 として扱う」のかを業務要件に応じて明確に判断してください。
-- COUNT(*): NULL を含む全行をカウント SELECT COUNT(*) FROM employees; -- 4(全行) -- COUNT(col): NULL 以外の行のみカウント SELECT COUNT(commission_pct) FROM employees; -- 2(NULL を除外) -- COUNT で NULL 行数を数える SELECT COUNT(*) - COUNT(commission_pct) AS null_count FROM employees;
実務パターン集
パターン①:計算式での NULL 回避
-- NULL を含む計算は結果が NULL になる
SELECT 100 + NULL AS result FROM dual; -- NULL
SELECT 100 * NULL AS result FROM dual; -- NULL
-- NVL で NULL を 0 に置き換えて計算
SELECT
employee_id,
salary + NVL(bonus, 0) + NVL(allowance, 0) AS total_pay
FROM employees;
-- ゼロ除算の防止(NULLIF)
SELECT order_amount / NULLIF(order_count, 0) AS avg_amount
FROM order_summary;
パターン②:文字列連結での NULL 回避
-- || 演算子: NULL が含まれると結果が NULL になることがある
-- Oracle では NULL || 'abc' = 'abc'(Oracle 独自仕様)
-- しかし明示的に NVL で処理する方が安全
SELECT
last_name || ' ' || NVL(first_name, '') AS full_name
FROM employees;
-- COALESCE で優先順位付きの連結
SELECT
COALESCE(nickname, first_name, 'ユーザー') || 'さん' AS display_name
FROM users;
パターン③:UPDATE で NULL を特定の値に一括変更
-- commission_pct が NULL の行を 0 に更新 UPDATE employees SET commission_pct = 0 WHERE commission_pct IS NULL; -- NVL を使ったパターン(NULLでない値はそのまま残す) UPDATE employees SET commission_pct = NVL(commission_pct, 0); -- 結果は同じだが、WHERE なしで全行を処理するため件数が多くなる
パターン④:ORDER BY での NULL の位置制御
-- Oracle のデフォルト: ASC では NULL は末尾、DESC では NULL は先頭 -- NULLS FIRST / NULLS LAST で明示制御(推奨) SELECT * FROM employees ORDER BY commission_pct ASC NULLS LAST; SELECT * FROM employees ORDER BY commission_pct DESC NULLS LAST; -- NVL を使ってソート順を制御する代替方法 -- NULL を最後にする(大きな値に置き換え) SELECT * FROM employees ORDER BY NVL(commission_pct, 99999) ASC;
パターン⑤:DECODE と NVL の組み合わせ
-- DECODE は NULL を「等しい」として扱える(= 演算子とは異なる)
SELECT DECODE(commission_pct, NULL, '固定給', '歩合あり') FROM employees;
-- これは NVL2 と同じ動作
-- NVL + DECODE の組み合わせ
SELECT NVL(
DECODE(status, 'A', '有効', 'I', '無効'),
'不明'
) AS status_name
FROM users;
DECODE 関数の詳細は「DECODE関数の使い方完全解説」を参照してください。
よくある質問
NVL(number_col, 'なし') のように NUMBER 列に文字列を渡すとエラーになります。型を揃えてください: NVL(TO_CHAR(number_col), 'なし')。NVL(CHAR列, 123) は 123 を CHAR に変換しますが、COALESCE(CHAR列, 123) はエラーになることがあります。NVL2(判定値, 非NULLの場合, NULLの場合) です。「判定値が NULL でない場合」が第 2 引数(先に書く)です。NVL とは順番が逆になりがちなので、混乱しやすい場合は CASE WHEN を使う方が可読性が上がります。SUM / AVG / MIN / MAX は NULL 行を自動的に除外します。SUM では NVL の有無で結果は変わりませんが、AVG では分母が変わるため結果が大きく異なります。NULL を 0 として扱いたい場合は AVG(NVL(col, 0)) と書き、NULL 行を除外したい場合はそのまま AVG(col) と書きます。a / NULLIF(b, 0) で b が 0 のとき NULL が返り、ゼロ除算エラーを回避できます。また、インポートデータで空文字列やプレースホルダー(例: ‘-‘)をNULL に変換するクレンジング処理にも使えます。まとめ
Oracle の NULL 処理関数の要点をまとめます。
| やりたいこと | 推奨関数 |
|---|---|
| NULL を代替値に置換 | NVL(col, 代替値) または COALESCE(col, 代替値) |
| NULL / 非 NULL で異なる値を返す | NVL2(col, 非NULL時の値, NULL時の値) |
| 複数候補から最初の非 NULL を取得 | COALESCE(a, b, c, …) |
| 特定の値を NULL に変換 | NULLIF(col, 特定値) |
| ゼロ除算を防止 | a / NULLIF(b, 0) |
| 計算式で NULL を 0 に | salary + NVL(bonus, 0) |
| AVG で NULL を 0 として扱う | AVG(NVL(col, 0)) |
| 他 RDBMS にも移植可能な書き方 | COALESCE(SQL 標準) |
