Oracle SQLのCOALESCE完全ガイド:NVLとの違い・使いどころ・落とし穴まで(図解と実行例つき)

Oracle Master Silver

結論

  • COALESCE(expr1, expr2, …) は「左から順にNULLでない最初の値」を返す、SQL標準の関数。
  • NVL の上位互換的に使える場面が多く、複数候補をスマートに書ける。
  • ただし 型の暗黙変換索引(インデックス)利用空文字=NULL(Oracleの仕様)には注意。
  • 実務では「表示名の決定」「連絡先のフォールバック」「集計の0埋め」「JOINキーの正規化」で真価を発揮。


NULLやNVLについてはこちら。

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

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


COALESCEとは(定義と基本構文)

定義
COALESCE(expr1, expr2, ..., exprN) は、左から順に評価してNULLでない最初の式の値を返します。すべてNULLならNULLを返します。

  • 型のルール:全ての引数は同じ型か、共通の型暗黙変換できる必要があります(CASE式と同様のルール)。
  • 空文字:OracleのSQLでは空文字”はNULLと同一に扱われます(VARCHAR2/CHAR)。つまり '' は実質的にNULLです。

基本構文

SELECT COALESCE(expr1, expr2, expr3) AS result
FROM   ...

図解:評価の流れ(左→右)

 ┌────────┐    NULL    ┌────────┐   非NULL   ┌────────┐
 │ expr1  │ ─────────► │ expr2  │ ─────────► │ 返却   │
 └────────┘            └────────┘            └────────┘
             (NULLなら次へ)       (最初に見つかった非NULLを返す)

※ 実務では副作用のある式意図しない暗黙変換を避けるため、後述のベストプラクティスに従うのが安全です。


NVL / NVL2 / CASEとの違い(何をいつ使う?)

NVLとの違い

  • NVL(a, b)2引数固定a がNULLなら b
  • COALESCE(a, b, c, ...)複数候補を一気に書ける。
  • 型決定
    • NVLは第1引数の型に合わせようとします。
    • COALESCEは共通型への変換(CASEと同様)。混在型は明示CASTが安全。

例(混在型はCAST推奨)

-- NGになり得る:文字列⇔数値の混在
SELECT COALESCE(number_col, varchar2_col, 0) FROM t;

-- OK:共通の型(文字列)に寄せる
SELECT COALESCE(TO_CHAR(number_col), varchar2_col, '0') FROM t;

NVL2との違い

  • NVL2(a, b, c)aがNULLでなければb、NULLならc
  • COALESCEは「最初の非NULL」を返すため、発想がやや異なる。
  • NULLでない時に別の式」という分岐は NVL2CASE WHEN a IS NOT NULL THEN ... END で書く方が明確。

CASEとの関係

  • COALESCE(a, b, c)CASE式で書けるパターンの糖衣構文。
  • 評価を厳密に制御したい(例:数値計算エラー回避のためのガード)場合は CASEが最も安全

代表的な使いどころ(実務パターン集)

1) 表示名の決定(ニックネーム>姓+名)

[nickname] があればそれ、なければ "first_name last_name"
SELECT COALESCE(nickname, first_name || ' ' || last_name) AS display_name
FROM   customers;

2) 連絡先のフォールバック(固定電話>携帯)

[phone] がNULLなら [mobile_phone]
SELECT COALESCE(phone, mobile_phone) AS main_contact
FROM   contacts;

3) 集計のNULLを0埋め(見た目を整える)

SELECT COALESCE(SUM(sales_amt), 0) AS total_amt
FROM   sales
WHERE  sales_date >= DATE '2025-08-01';

4) JOINキーの正規化(複数列の候補で結合)

-- 顧客コードがなければ代替コードでマスタにJOIN、など
SELECT o.order_id, m.name
FROM   orders o
LEFT JOIN master m
       ON m.code = COALESCE(o.customer_code, o.customer_code_alt);

5) インデックス設計との相性(関数ベース索引/仮想列)

  • WHERE COALESCE(col1, col2) = :x のような関数適用は、通常の索引が効きづらい。
  • 仮想列索引、または関数ベース索引を作ると検索が速くなることがある(後述の実例参照)。

落とし穴とベストプラクティス

空文字はNULL

  • INSERT ... VALUES ('' )NULLと同じCOALESCE('', 'A')'A' を返す。

型の暗黙変換に注意

  • 文字列⇔数値、文字列⇔日付が混在するとORA-01722(数値無効)などの原因。
  • 明示的な TO_CHAR / TO_NUMBER / TO_DATE を使い、共通型に寄せる。

計算エラーの回避

  • 0除算など「評価したくない式」は CASEでガード
-- 0除算の可能性がある場合
SELECT CASE
         WHEN denominator = 0 OR denominator IS NULL THEN NULL
         ELSE numerator / denominator
       END AS safe_ratio
FROM   t;

索引の活用

  • 述語に COALESCE(col1, col2) を書くと通常の索引が効かないことがある。
  • 仮想列 + 索引、または 関数ベース索引を検討。

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

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


図解(テキスト)

使い分け早見図

┌───────────┬──────────────────────────────┬──────────────────────────┐
│ 目的       │ 推奨関数                      │ メモ                      │
├───────────┼──────────────────────────────┼──────────────────────────┤
│ 候補が3つ以上 │ COALESCE(a,b,c,...)            │ 一番左の非NULLを返す         │
│ 2つだけ     │ NVL(a,b)                       │ aがNULLならb               │
│ 分岐(aが非NULLならX)│ NVL2(a, x, y)                 │ aのNULL/非NULLで二者択一      │
│ ガード付き  │ CASE WHEN ... THEN ... END     │ 評価順と条件を明確に制御      │
└───────────┴──────────────────────────────┴──────────────────────────┘

実行例(ユーザー作成・権限・テーブル・INSERT付き)

例はPDB上の任意スキーマで実施します。CDB環境の場合は先に対象PDBへ接続してください。
(例:sqlplus sys/<pwd>@pdb1 as sysdbaCREATE USER...

1) 準備:ユーザー作成と権限付与

-- SYSDBAで実行(対象PDB上)
CREATE USER COALESCE_DEMO IDENTIFIED BY "coalesce_demo"
  DEFAULT TABLESPACE users
  TEMPORARY TABLESPACE temp
  QUOTA UNLIMITED ON users;

GRANT CREATE SESSION, CREATE TABLE, CREATE VIEW, CREATE SEQUENCE TO COALESCE_DEMO;

-- (必要に応じて)接続
-- CONNECT COALESCE_DEMO/coalesce_demo@<サービス名>;

2) 表示名の決定:customers(仮想列でCOALESCE)

表定義(CREATE TABLE)

-- COALESCEを使った仮想列 display_name を定義
CREATE TABLE customers (
  customer_id   NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY PRIMARY KEY,
  first_name    VARCHAR2(30) NOT NULL,
  last_name     VARCHAR2(30) NOT NULL,
  nickname      VARCHAR2(30),
  email         VARCHAR2(100),
  display_name  GENERATED ALWAYS AS
                (COALESCE(nickname, first_name || ' ' || last_name)) VIRTUAL
);

データ投入(INSERT)

INSERT INTO customers (first_name, last_name, nickname, email)
VALUES ('TARO',  'YAMADA', NULL, 'taro@example.com');

INSERT INTO customers (first_name, last_name, nickname, email)
VALUES ('HANAKO','SUZUKI', 'Hana', 'hana@example.com');

-- 空文字はNULLとして扱われる(表示名は first||' '||last が使われる)
INSERT INTO customers (first_name, last_name, nickname, email)
VALUES ('JIRO',  'SATO',   '',    'jiro@example.com');

COMMIT;

確認(SELECT)

SELECT customer_id, first_name, last_name, nickname, display_name
FROM   customers
ORDER  BY customer_id;

想定出力(イメージ)

CUSTOMER_ID  FIRST_NAME  LAST_NAME  NICKNAME  DISPLAY_NAME
----------   ----------  ---------  --------  -------------------
1            TARO        YAMADA               TARO YAMADA
2            HANAKO      SUZUKI     Hana      Hana
3            JIRO        SATO                 JIRO SATO

索引(仮想列に索引を付与して検索を高速化)

CREATE INDEX ix_customers_display_name ON customers(display_name);

3) 連絡先フォールバック:contacts(仮想列+索引)

表定義(CREATE TABLE)

CREATE TABLE contacts (
  contact_id    NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY PRIMARY KEY,
  phone         VARCHAR2(20),
  mobile_phone  VARCHAR2(20),
  main_contact  GENERATED ALWAYS AS (COALESCE(phone, mobile_phone)) VIRTUAL
);

CREATE INDEX ix_contacts_main_contact ON contacts(main_contact);

データ投入(INSERT)

INSERT INTO contacts (phone, mobile_phone) VALUES (NULL,      '090-1111-2222');
INSERT INTO contacts (phone, mobile_phone) VALUES ('03-1234-5678', NULL);
INSERT INTO contacts (phone, mobile_phone) VALUES (NULL,      NULL);
COMMIT;

確認(SELECT)

SELECT contact_id, phone, mobile_phone, main_contact
FROM   contacts
ORDER  BY contact_id;

想定出力(イメージ)

CONTACT_ID  PHONE         MOBILE_PHONE   MAIN_CONTACT
----------  ------------  -------------  -------------
1                          090-1111-2222 090-1111-2222
2           03-1234-5678                  03-1234-5678
3                                         (NULL)

4) 集計の0埋め:sales

表定義(CREATE TABLE)

CREATE TABLE sales (
  id         NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY PRIMARY KEY,
  sales_date DATE NOT NULL,
  sales_amt  NUMBER(12,2)
);

データ投入(INSERT)

INSERT INTO sales (sales_date, sales_amt) VALUES (DATE '2025-08-14',  5000);
INSERT INTO sales (sales_date, sales_amt) VALUES (DATE '2025-08-15',  NULL);
INSERT INTO sales (sales_date, sales_amt) VALUES (DATE '2025-08-15',  7000);
COMMIT;

NULLを0にして合計

SELECT COALESCE(SUM(sales_amt), 0) AS total_amt
FROM   sales
WHERE  sales_date >= DATE '2025-08-15';

想定出力

TOTAL_AMT
---------
7000

5) 混在型は明示変換:安全なCOALESCE

-- 数値/文字が混じる場合はTO_CHARなどで統一
SELECT COALESCE(TO_CHAR(number_col), varchar2_col, 'N/A') AS val
FROM   your_table;

実務Tips(パフォーマンス&可読性)

  • 可読性:候補が3つ以上あるときはCOALESCEの方が短く読みやすい
  • 索引:述語や並べ替えに使うなら仮想列+索引、または関数ベース索引を検討。
  • 型の明示暗黙変換の回避は品質・性能の両面で効く。
  • 空文字:アプリ層が '' を送ってくる場合でもNULLとして扱われることを前提に設計。
  • 安全な分岐エラー回避や評価制御CASE WHEN が最強(COALESCE/NVLは分岐というより「候補列の選択」)。

よくある質問(FAQ)

Q1. NVLとCOALESCEはどっちを使う?
A. 候補が2つだけならNVLでも可。3つ以上ならCOALESCEが読みやすい。チーム規約でCOALESCEに統一もおすすめ。

Q2. 空文字とNULLの違いは?
A. OracleのSQLでは空文字はNULLと同じCOALESCE('', 'x')'x' を返す。

Q3. 索引は効く?
A. COALESCE(col1, col2)通常索引は効きにくい仮想列+索引または関数ベース索引で対応。


まとめ

  • COALESCEは「左から順の最初の非NULL」を返すSQL標準関数。
  • NVLより候補が多い時に可読性が高い。型は明示変換で安全に。
  • 空文字=NULL(Oracle仕様)を前提に設計する。
  • 仮想列+索引検索・並べ替えを高速化できる。
  • エラー回避や厳密制御CASE WHEN を使う。


[参考]
COALESCE

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

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

コメント

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