「SQLでNULL値が出てきて計算がおかしくなる…」
「NULLの時だけデフォルト値を表示したい」
「複数のカラムから最初の有効な値を取得したい」
「CASE文を使うと複雑になりすぎる」
そんな悩みを解決するのがCOALESCE関数です!
COALESCEは「NULL値を別の値に置き換える」という、シンプルだけど超強力な関数。これをマスターすれば、NULL値に悩まされることがなくなります。
この記事では、COALESCEの基本から応用まで、実践的な例を交えながら分かりやすく解説していきます!
COALESCEとは?基本を理解しよう

COALESCEの基本構文
COALESCE(値1, 値2, 値3, ...)
動作原理: 左から順番に評価して、最初にNULLではない値を返す
簡単な例で理解する
-- 基本例
SELECT COALESCE(NULL, 'デフォルト値');
-- 結果: 'デフォルト値'
SELECT COALESCE('実際の値', 'デフォルト値');
-- 結果: '実際の値'
SELECT COALESCE(NULL, NULL, NULL, '最後の値');
-- 結果: '最後の値'
-- 全部NULLの場合
SELECT COALESCE(NULL, NULL, NULL);
-- 結果: NULL
なぜCOALESCEが必要?
NULL値の問題点:
-- NULL値との計算は全てNULLになる
SELECT 100 + NULL; -- 結果: NULL
SELECT 'Hello' || NULL; -- 結果: NULL
-- COALESCEで解決
SELECT 100 + COALESCE(NULL, 0); -- 結果: 100
SELECT 'Hello' || COALESCE(NULL, ''); -- 結果: 'Hello'
実践!よく使うCOALESCEパターン
パターン1:デフォルト値の設定
-- ユーザーテーブルの例
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
nickname VARCHAR(100),
email VARCHAR(200),
phone VARCHAR(20)
);
-- ニックネームがない場合は本名を表示
SELECT
name,
COALESCE(nickname, name) AS display_name
FROM users;
-- 連絡先の優先順位(メール → 電話 → 「連絡先なし」)
SELECT
name,
COALESCE(email, phone, '連絡先なし') AS contact
FROM users;
パターン2:計算でのNULL対策
-- 売上テーブル
CREATE TABLE sales (
id SERIAL PRIMARY KEY,
product_name VARCHAR(100),
price DECIMAL(10,2),
discount DECIMAL(10,2),
quantity INTEGER
);
-- 割引がNULLの場合は0として計算
SELECT
product_name,
price,
COALESCE(discount, 0) AS discount,
price - COALESCE(discount, 0) AS final_price,
(price - COALESCE(discount, 0)) * quantity AS total
FROM sales;
-- 平均計算でNULLを除外
SELECT
AVG(COALESCE(discount, 0)) AS avg_discount
FROM sales;
パターン3:文字列結合でのNULL処理
-- 住所テーブル
CREATE TABLE addresses (
id SERIAL PRIMARY KEY,
prefecture VARCHAR(50),
city VARCHAR(50),
town VARCHAR(50),
building VARCHAR(100)
);
-- NULL値を空文字に変換して結合
SELECT
COALESCE(prefecture, '') ||
COALESCE(city, '') ||
COALESCE(town, '') ||
COALESCE(building, '') AS full_address
FROM addresses;
-- より見やすく整形
SELECT
CONCAT_WS(' ',
COALESCE(prefecture, ''),
COALESCE(city, ''),
COALESCE(town, ''),
COALESCE(building, '')
) AS formatted_address
FROM addresses;
COALESCEの応用テクニック
複数テーブルの結合でのNULL処理
-- 商品と在庫の結合
CREATE TABLE products (
id SERIAL PRIMARY KEY,
name VARCHAR(100),
default_price DECIMAL(10,2)
);
CREATE TABLE inventory (
product_id INTEGER,
current_price DECIMAL(10,2),
stock_quantity INTEGER
);
-- LEFT JOINでNULLが発生する場合の対処
SELECT
p.name,
COALESCE(i.current_price, p.default_price, 0) AS selling_price,
COALESCE(i.stock_quantity, 0) AS stock,
CASE
WHEN COALESCE(i.stock_quantity, 0) = 0 THEN '在庫なし'
WHEN COALESCE(i.stock_quantity, 0) < 10 THEN '在庫わずか'
ELSE '在庫あり'
END AS stock_status
FROM products p
LEFT JOIN inventory i ON p.id = i.product_id;
日付のNULL処理
-- イベントテーブル
CREATE TABLE events (
id SERIAL PRIMARY KEY,
event_name VARCHAR(200),
scheduled_date DATE,
actual_date DATE,
cancelled_date DATE
);
-- 実際の日付 → 予定日 → 現在日付の優先順位
SELECT
event_name,
COALESCE(actual_date, scheduled_date, CURRENT_DATE) AS event_date,
COALESCE(
cancelled_date::TEXT,
actual_date::TEXT,
'予定: ' || scheduled_date::TEXT
) AS status
FROM events;
-- 日付の差分計算
SELECT
event_name,
COALESCE(actual_date, scheduled_date) AS final_date,
CURRENT_DATE - COALESCE(actual_date, scheduled_date, CURRENT_DATE) AS days_ago
FROM events;
JSON/JSONBでのCOALESCE
-- JSON設定テーブル
CREATE TABLE user_settings (
user_id INTEGER PRIMARY KEY,
preferences JSONB
);
-- JSONフィールドのNULL対策
SELECT
user_id,
COALESCE(preferences->>'theme', 'default') AS theme,
COALESCE(preferences->>'language', 'ja') AS language,
COALESCE((preferences->>'notifications')::BOOLEAN, true) AS notifications
FROM user_settings;
-- JSON全体のデフォルト値
SELECT
user_id,
COALESCE(preferences, '{"theme":"default","language":"ja"}'::JSONB) AS settings
FROM user_settings;
COALESCEと他の関数の比較
COALESCE vs CASE文
-- CASE文での実装(冗長)
SELECT
CASE
WHEN column1 IS NOT NULL THEN column1
WHEN column2 IS NOT NULL THEN column2
WHEN column3 IS NOT NULL THEN column3
ELSE 'デフォルト'
END AS result
FROM table_name;
-- COALESCEなら簡潔
SELECT
COALESCE(column1, column2, column3, 'デフォルト') AS result
FROM table_name;
COALESCE vs NULLIF
-- NULLIFは特定の値をNULLに変換
SELECT NULLIF('', ''); -- 結果: NULL
SELECT NULLIF('value', ''); -- 結果: 'value'
-- 組み合わせて使う
SELECT
COALESCE(NULLIF(TRIM(comment), ''), 'コメントなし') AS comment_display
FROM reviews;
-- 空文字や空白のみの場合も「コメントなし」と表示
COALESCE vs IFNULL(MySQL互換性)
-- PostgreSQLにはIFNULLはない
-- 代わりにCOALESCEを使用
-- MySQLスタイル(動作しない)
-- SELECT IFNULL(column1, 'default');
-- PostgreSQLスタイル
SELECT COALESCE(column1, 'default');
実務でよく使うCOALESCEパターン集

ランキング表示での活用
-- スコアテーブル
CREATE TABLE scores (
user_id INTEGER,
game_id INTEGER,
score INTEGER,
bonus_score INTEGER
);
-- 総合スコアでランキング
SELECT
user_id,
COALESCE(score, 0) + COALESCE(bonus_score, 0) AS total_score,
RANK() OVER (ORDER BY COALESCE(score, 0) + COALESCE(bonus_score, 0) DESC) AS ranking
FROM scores;
集計関数との組み合わせ
-- 売上集計
CREATE TABLE monthly_sales (
month DATE,
region VARCHAR(50),
sales_amount DECIMAL(12,2),
target_amount DECIMAL(12,2)
);
-- 達成率の計算(0除算対策込み)
SELECT
region,
SUM(COALESCE(sales_amount, 0)) AS total_sales,
SUM(COALESCE(target_amount, 0)) AS total_target,
CASE
WHEN SUM(COALESCE(target_amount, 0)) = 0 THEN 0
ELSE ROUND(
SUM(COALESCE(sales_amount, 0)) * 100.0 /
SUM(COALESCE(target_amount, 0)), 2
)
END AS achievement_rate
FROM monthly_sales
GROUP BY region;
動的なデフォルト値
-- 設定テーブルからデフォルト値を取得
WITH defaults AS (
SELECT
'pending' AS default_status,
0 AS default_priority
)
SELECT
t.id,
COALESCE(t.status, d.default_status) AS status,
COALESCE(t.priority, d.default_priority) AS priority
FROM tasks t
CROSS JOIN defaults d;
パフォーマンスの考慮点
インデックスとCOALESCE
-- インデックスが効かない例
CREATE INDEX idx_nullable_column ON table_name(nullable_column);
-- このクエリではインデックスが使われない可能性
SELECT * FROM table_name
WHERE COALESCE(nullable_column, 'default') = 'value';
-- 改善案:部分インデックス
CREATE INDEX idx_not_null ON table_name(nullable_column)
WHERE nullable_column IS NOT NULL;
-- または関数インデックス
CREATE INDEX idx_coalesce ON table_name(COALESCE(nullable_column, 'default'));
大量データでの注意点
-- 非効率な例(全行でCOALESCE実行)
SELECT
COALESCE(large_text_column, 'デフォルトの長いテキスト...') AS text
FROM large_table;
-- 効率的な例(必要な行だけ処理)
SELECT
CASE
WHEN large_text_column IS NULL THEN 'デフォルトの長いテキスト...'
ELSE large_text_column
END AS text
FROM large_table
WHERE condition = true; -- 条件で絞り込み
よくあるミスと対処法
データ型の不一致
-- エラーになる例
SELECT COALESCE(integer_column, 'デフォルト'); -- 型が違う
-- 正しい例
SELECT COALESCE(integer_column::TEXT, 'デフォルト');
-- または
SELECT COALESCE(integer_column, 0);
空文字とNULLの混在
-- 空文字はNULLではない
SELECT COALESCE('', 'デフォルト'); -- 結果: '' (空文字)
-- 空文字もデフォルト値にしたい場合
SELECT
CASE
WHEN column_name IS NULL OR column_name = '' THEN 'デフォルト'
ELSE column_name
END AS result
-- または
SELECT COALESCE(NULLIF(column_name, ''), 'デフォルト');
配列やJSONでの落とし穴
-- 空配列はNULLではない
SELECT COALESCE(ARRAY[]::INTEGER[], ARRAY[1,2,3]); -- 結果: {} (空配列)
-- 配列要素数で判定
SELECT
CASE
WHEN array_length(array_column, 1) IS NULL THEN ARRAY[1,2,3]
ELSE array_column
END AS result;
実践演習問題
問題1:社員情報の整形
-- テーブル作成
CREATE TABLE employees (
id SERIAL PRIMARY KEY,
first_name VARCHAR(50),
last_name VARCHAR(50),
middle_name VARCHAR(50),
preferred_name VARCHAR(100),
department VARCHAR(50),
salary DECIMAL(10,2)
);
-- 解答例:表示名の優先順位を実装
SELECT
id,
COALESCE(
preferred_name,
CONCAT_WS(' ', first_name, middle_name, last_name),
'Unknown Employee'
) AS display_name,
COALESCE(department, '未配属') AS dept,
COALESCE(salary, 0) AS salary
FROM employees;
問題2:在庫管理システム
-- 複数倉庫の在庫を統合
SELECT
product_id,
product_name,
COALESCE(warehouse_a_stock, 0) +
COALESCE(warehouse_b_stock, 0) +
COALESCE(warehouse_c_stock, 0) AS total_stock,
COALESCE(
minimum_stock,
CASE
WHEN product_category = 'essential' THEN 100
ELSE 10
END
) AS required_minimum
FROM inventory_master;
まとめ – COALESCEでNULL値マスターになろう!
COALESCEの使い方について、基本から応用まで解説してきました。
重要ポイント:
- COALESCEは最初の非NULL値を返す
- デフォルト値の設定に最適
- 計算や文字列結合でのNULL対策に必須
- CASE文より簡潔に書ける
- パフォーマンスに注意が必要な場合もある
使いどころ:
- レポート作成でのNULL値表示
- 計算処理でのエラー回避
- データ移行での欠損値補完
- API応答のデフォルト値設定
- ユーザー設定のフォールバック
COALESCEをマスターすれば、NULL値に悩まされることなく、クリーンで堅牢なSQLを書けるようになります。
ぜひ実際のプロジェクトで活用してみてください!
コメント