【Oracle】NULL処理関数完全ガイド|NVL・NVL2・COALESCE・NULLIF・LNNVL の違いと使い分け・NOT IN の落とし穴まで解説

【Oracle】NULL処理関数完全ガイド|NVL・NVL2・COALESCE・NULLIF・LNNVL の違いと使い分け・NOT IN の落とし穴まで解説 Oracle

Oracle の NULL は「値が存在しない」ことを表す特殊な状態です。NULL は数値 0 でも空文字列でもなく、あらゆる比較演算が UNKNOWN(真でも偽でもない第3の状態)になります。この挙動を理解せずに SQL を書くと、データが意図せず除外される不具合が発生します。

本記事では Oracle で利用できる NULL 処理関数(NVL・NVL2・COALESCE・NULLIF・LNNVL)の仕様と使い分けを、実際によく発生するトラブルパターンとともに解説します。

この記事でわかること

  • Oracle における NULL の性質(3値論理・比較演算の挙動)
  • NVL / NVL2 / COALESCE / NULLIF の違いと適切な使い分け
  • DECODE での NULL 処理
  • LNNVL:WHERE 句で NULL を含む条件を正しく扱う方法
  • NOT IN に NULL が含まれると全件除外される落とし穴
  • NULL を含む集計(COUNT・SUM・AVG)の挙動
  • NULL 安全な結合・ソート・グループ化のパターン
スポンサーリンク

Oracle における NULL の基本性質

Oracle の NULL は SQL 標準に基づいた3値論理(TRUE / FALSE / UNKNOWN)に従います。

結果 理由
NULL = NULL UNKNOWN NULL は「不明な値」どうしの比較も不明
NULL != NULL UNKNOWN 同上
NULL = 0 UNKNOWN NULL と任意の値の比較は UNKNOWN
NULL > 0 UNKNOWN 同上
NULL IS NULL TRUE IS NULL は NULL チェック専用の構文
NULL IS NOT NULL FALSE 同上
1 + NULL NULL NULL を含む算術演算は常に NULL
'abc' || NULL ‘abc’ Oracle独自仕様:文字列連結では NULL を空文字列として扱う
NULL の挙動を確認する
-- WHERE 条件が UNKNOWN になると行が返らない
SELECT * FROM employees WHERE manager_id = NULL;     -- 0行(NULL=NULLはUNKNOWN)
SELECT * FROM employees WHERE manager_id IS NULL;    -- NULL行が返る(正しい書き方)

-- NULL を含む算術演算
SELECT 100 + NULL  FROM dual;   -- NULL
SELECT 100 - NULL  FROM dual;   -- NULL
SELECT 100 * NULL  FROM dual;   -- NULL
SELECT 100 / NULL  FROM dual;   -- NULL

-- 文字列連結の Oracle 独自挙動(他 RDBMS と異なる)
SELECT 'Hello' || NULL || ' World' FROM dual;   -- 'Hello World'(NULLが無視される)
-- ※ PostgreSQL/MySQL では NULL を含む連結は NULL になる

NULL 処理関数の一覧と使い分け早見表

関数 構文 動作 SQL標準
NVL NVL(val, default) val が NULL なら default を返す Oracle独自
NVL2 NVL2(val, not_null, null_val) val が NULL でなければ not_null、NULL なら null_val を返す Oracle独自
COALESCE COALESCE(val1, val2, ...) 左から順に評価し、最初の非 NULL を返す SQL標準(推奨)
NULLIF NULLIF(val1, val2) val1 = val2 なら NULL、それ以外は val1 を返す SQL標準
LNNVL LNNVL(condition) 条件が FALSE または UNKNOWN なら TRUE を返す Oracle独自

NVL:NULL を別の値に置き換える基本関数

最もシンプルな NULL 置換関数です。NVL(値, デフォルト値) で、値が NULL の場合にデフォルト値を返します。両引数はデータ型が一致(または暗黙変換可能)である必要があります。

NVL の基本使用例
-- 基本: NULL を 0 に置換
SELECT employee_id,
       NVL(commission_pct, 0) AS commission
FROM employees;

-- 文字列の NULL を別の文字列に置換
SELECT employee_id,
       NVL(TO_CHAR(manager_id), '(トップ)') AS manager
FROM employees;

-- NVL を使った計算(NULL が混在しても正しく集計できる)
SELECT employee_id,
       salary + NVL(commission_pct, 0) * salary AS total_income
FROM employees;
-- ※ commission_pct が NULL の場合、NVL なしだと total_income が NULL になる

-- WHERE 句での使用(NULL を含む列に条件をかける場合)
SELECT * FROM products
WHERE NVL(discontinued, 'N') = 'N';  -- discontinued が NULL のものも含める
NVL のパフォーマンス注意点
NVL(expr, default)exprdefault両方を必ず評価します。default 部分が重いサブクエリや関数呼び出しの場合、expr が NULL でなくても実行されます。この場合は CASE WHEN を使うか、後述の COALESCE(短絡評価)を検討してください。

NVL2:NULL か否かで異なる値を返す

NVL2(値, NULLでない場合, NULLの場合) は、CASE WHEN IS NULL の短縮形です。NVL(val, default) では「NULL 以外のときは元の値をそのまま使う」しかできませんが、NVL2 は NULL 以外の場合も別の値に変換できます。

NVL2 の使用例
-- 基本: commission_pct が NULL かどうかで表示を切り替える
SELECT employee_id,
       NVL2(commission_pct,
            'コミッションあり',    -- NULL でない場合
            'コミッションなし')    -- NULL の場合
       AS commission_status
FROM employees;

-- NVL2 と NVL の比較
-- NVL:  NULL → default、NULL 以外 → 元の値(そのまま)
-- NVL2: NULL → null_val、NULL 以外 → not_null_val(変換も可能)

-- 数値変換例: NULL なら 0、NULL でなければ 1
SELECT employee_id,
       NVL2(manager_id, 1, 0) AS has_manager
FROM employees;

-- CASE WHEN と同等(NVL2 の方が簡潔)
SELECT employee_id,
       CASE WHEN commission_pct IS NOT NULL THEN 'あり'
            ELSE 'なし'
       END AS commission_status
FROM employees;

COALESCE:複数の候補から最初の非 NULL を返す(SQL 標準・推奨)

COALESCE(val1, val2, ..., valN) は、引数を左から順に評価し最初の非 NULL 値を返します。SQL 標準関数のため、PostgreSQL・MySQL・SQL Server など他の RDBMS にも移植しやすいため、NVL より COALESCE を推奨します。

COALESCE の基本使用例
-- 優先順位のある NULL 処理: preferred_name → nickname → last_name の順で最初の非NULLを使う
SELECT employee_id,
       COALESCE(preferred_name, nickname, last_name) AS display_name
FROM employees;

-- NVL との比較(引数2個のときは同じ動作)
SELECT NVL(commission_pct, 0)      AS nvl_result,
       COALESCE(commission_pct, 0) AS coalesce_result
FROM employees;
-- 結果は同じだが COALESCE は SQL 標準なので移植性が高い

-- 複数テーブルの列をフォールバック的に参照
SELECT o.order_id,
       COALESCE(o.delivery_address, c.default_address, '住所未登録') AS ship_to
FROM orders o
JOIN customers c ON o.customer_id = c.customer_id;
COALESCE の短絡評価(パフォーマンス優位点)
-- COALESCE は最初の非NULLが見つかった時点で残りの評価を打ち切る(短絡評価)
-- 重い処理のデフォルト値を後ろに置くと、前の値が非NULLなら実行されない

SELECT product_id,
       COALESCE(
           cached_price,                           -- まずキャッシュ価格を確認
           (SELECT price FROM price_master         -- なければマスターから取得(重い)
            WHERE product_id = p.product_id),
           0                                       -- それもなければ 0
       ) AS effective_price
FROM products p;

-- NVL は両方必ず評価するため、上記のようなパターンは COALESCE が有利

NULLIF:特定の値を NULL に変換する

NULLIF(val1, val2)val1 = val2 のとき NULL を返し、それ以外は val1 を返します。「ゼロ除算回避」や「特定のダミー値を NULL に変換する」ときによく使います。

NULLIF の基本使用例
-- ゼロ除算を回避する(NULLIF で分母が 0 のとき NULL にする)
SELECT department_id,
       total_sales / NULLIF(headcount, 0) AS avg_sales_per_person
FROM dept_summary;
-- headcount が 0 なら NULLIF(headcount, 0) = NULL → 割り算が NULL になる(ORA-01476 を回避)

-- ダミー値(-1 や 'N/A')を NULL に変換して正規化
SELECT product_id,
       NULLIF(price, -1)      AS price,      -- -1 は未設定のダミー値 → NULL に変換
       NULLIF(category, 'N/A') AS category   -- 'N/A' → NULL に変換
FROM raw_import;

-- NULLIF と DECODE の比較
-- NULLIF(v, x) ≡ CASE WHEN v = x THEN NULL ELSE v END
-- DECODE(v, x, NULL, v) と同義だが NULLIF の方が意図が明確

-- 更新時に特定の値を NULL にリセット
UPDATE employees
SET manager_id = NULLIF(p_manager_id, 0)   -- 0 が入力された場合は NULL(トップ扱い)
WHERE employee_id = p_emp_id;

LNNVL:NULL を含む条件を正しく扱う WHERE 句専用関数

LNNVL(condition) は条件が FALSE または UNKNOWN(NULL を含む比較)のとき TRUE を返します。「列の値が x ではない、または NULL である」行を取得したいときに、IS NULL との OR 記述を省略できます。

LNNVL の使用例
-- LNNVL なしの書き方(冗長)
SELECT * FROM employees
WHERE department_id != 10
   OR department_id IS NULL;   -- department_id が NULL の行も含めたい

-- LNNVL を使った書き方(簡潔)
SELECT * FROM employees
WHERE LNNVL(department_id = 10);
-- department_id = 10 が FALSE(10 以外)または UNKNOWN(NULL)の行を返す

-- 実務パターン: バッチ処理で「処理済み(is_processed=1)以外」を取得
SELECT * FROM work_queue
WHERE LNNVL(is_processed = 1);
-- is_processed が 0 または NULL(未設定)の行をすべて取得

NOT IN に NULL が含まれると全件除外される落とし穴

Oracle の NULL に関するトラブルで最も影響が大きいのが NOT IN のサブクエリに NULL が含まれる問題です。この挙動は SQL 標準に基づくもので、NULL の3値論理から必然的に生じます。

NOT IN に NULL が含まれると0件になる例
-- サンプルデータ
CREATE TABLE depts (dept_id NUMBER);
INSERT INTO depts VALUES (10);
INSERT INTO depts VALUES (20);
INSERT INTO depts VALUES (NULL);   -- NULL が1件含まれている

-- IN は問題なく動作する
SELECT * FROM employees
WHERE department_id IN (SELECT dept_id FROM depts);
-- → department_id が 10 または 20 の行が返る(NULLはスキップ)

-- NOT IN はサブクエリに NULL が1件でもあると全件が除外される
SELECT * FROM employees
WHERE department_id NOT IN (SELECT dept_id FROM depts);
-- → 0件(!)
-- 理由: NOT IN (10, 20, NULL) は
--       department_id != 10 AND department_id != 20 AND department_id != NULL
--       最後の "department_id != NULL" が UNKNOWN になるため
--       全体が UNKNOWN → WHERE 条件を満たさない → 全件除外

-- 対処法1: WHERE 句で NULL を除外する
SELECT * FROM employees
WHERE department_id NOT IN (
    SELECT dept_id FROM depts WHERE dept_id IS NOT NULL
);

-- 対処法2: NOT EXISTS を使う(NULL に対して安全)
SELECT * FROM employees e
WHERE NOT EXISTS (
    SELECT 1 FROM depts d
    WHERE d.dept_id = e.department_id   -- NULL = NULL は FALSE なのでマッチしない
);
-- NOT EXISTS は NULL を自動的に安全に処理するため推奨
NOT IN より NOT EXISTS を推奨する理由
サブクエリの結果に NULL が含まれる可能性がある場合、NOT IN意図せず全件除外されるバグの原因になります。NOT EXISTS は NULL に対して安全な動作をするため、サブクエリを使った否定条件には NOT EXISTS を使う習慣をつけることを推奨します。

NULL を含む集計関数の挙動

Oracle の集計関数(COUNT・SUM・AVG等)は NULL を無視します。この挙動が意図しない集計結果を生む場合があります。

集計関数と NULL の挙動
-- テストデータ(salary に NULL が含まれる)
-- emp_id: 1, salary: 300000
-- emp_id: 2, salary: 400000
-- emp_id: 3, salary: NULL    ← 給与未設定

SELECT COUNT(*)           AS total_rows,     -- 3(NULL も含めて行数カウント)
       COUNT(salary)      AS salary_count,   -- 2(NULL を除いた件数)
       SUM(salary)        AS total_salary,   -- 700000(NULL を無視)
       AVG(salary)        AS avg_salary,     -- 350000(700000 / 2 ← NULL を除いた件数で割る)
       AVG(NVL(salary,0)) AS avg_with_zero   -- 233333(700000 / 3 ← 全員で割りたい場合)
FROM employees;
-- ポイント: AVG(salary) は NULL を除いた行の平均になる点に注意
-- 「全員の平均」が必要なら NVL(salary, 0) でゼロ補完してから AVG

-- GROUP BY での NULL: Oracle では NULL もひとつのグループとして扱われる
SELECT department_id, COUNT(*) AS headcount
FROM employees
GROUP BY department_id
ORDER BY department_id NULLS LAST;  -- NULL グループを最後に

DECODE での NULL 処理

Oracle の DECODE 関数は NULL を = で比較しても正しく処理します。これは通常の = NULL が UNKNOWN になるルールの例外で、DECODE 内部では NULL 同士の比較が TRUE になります。

DECODE での NULL 比較
-- DECODE は NULL == NULL を TRUE として扱う(通常の = とは異なる)
SELECT employee_id,
       DECODE(manager_id,
              NULL, 'トップ管理職',      -- manager_id が NULL の場合
              manager_id)               -- NULL でない場合はそのまま
       AS manager_label
FROM employees;

-- CASE WHEN では IS NULL が必要(通常のルール)
SELECT employee_id,
       CASE WHEN manager_id IS NULL THEN 'トップ管理職'
            ELSE TO_CHAR(manager_id)
       END AS manager_label
FROM employees;

-- どちらも同じ結果を返す。CASE WHEN の方が意図が明確でSQL標準準拠のため推奨

ORDER BY と NULL:NULLS FIRST / NULLS LAST

Oracle は ORDER BY で NULL の位置を制御する NULLS FIRST / NULLS LAST 句をサポートします(Oracle 9i 以降)。

NULLS FIRST / NULLS LAST の使い方
-- デフォルト: Oracle では昇順(ASC)で NULL は最後、降順(DESC)で NULL は最初
SELECT employee_id, salary
FROM employees
ORDER BY salary ASC;   -- NULL が最後(Oracleのデフォルト)

-- 明示的に制御
SELECT employee_id, salary
FROM employees
ORDER BY salary ASC  NULLS FIRST;   -- NULL を先頭に
ORDER BY salary ASC  NULLS LAST;    -- NULL を末尾に(昇順デフォルトと同じ)
ORDER BY salary DESC NULLS FIRST;   -- 降順でも NULL を先頭に(降順デフォルトと同じ)
ORDER BY salary DESC NULLS LAST;    -- 降順で NULL を末尾に

-- 分析関数でも NULLS FIRST / NULLS LAST が使える
SELECT employee_id, salary,
       RANK() OVER (ORDER BY salary DESC NULLS LAST) AS salary_rank
FROM employees;

NULL 処理関数の使い分けまとめ

状況別の推奨関数

やりたいこと 推奨関数
NULL を別の値に置換(1対1) COALESCE / NVL COALESCE(salary, 0)
複数の候補から最初の非NULLを選ぶ COALESCE COALESCE(a, b, c, 0)
NULL か否かで別の値を返す NVL2 / CASE WHEN NVL2(col, 'あり', 'なし')
特定の値を NULL に変換 NULLIF NULLIF(price, -1)
ゼロ除算を NULL に変換して回避 NULLIF a / NULLIF(b, 0)
NULL を含む否定条件 NOT EXISTS / LNNVL WHERE LNNVL(col = 10)
サブクエリでの否定(NULL安全) NOT EXISTS WHERE NOT EXISTS (...)

まとめ

Oracle における NULL は「3値論理」に基づいており、その挙動を正しく理解することが堅牢な SQL 設計の基本です。

特に注意すべき点は以下の2つです。

  • NOT IN のサブクエリに NULL が含まれると全件除外される:常に NOT EXISTS で代替することを推奨
  • AVG は NULL を除外した件数で割る:全員を母数にしたい場合は NVL でゼロ補完

NULL 置換関数は SQL 標準の COALESCE を基本とし、Oracle 固有の NVL2NULLIF を用途に応じて組み合わせてください。NVL 関数の基本的な使い方はNVL関数でNULLを指定した値に置き換える方法も合わせて参照してください。