「NULL値を0に置き換えたい」 「空文字をNULLに変更したい」 「NULLの行だけを更新したいけど、うまくいかない…」
PostgreSQLでNULL値を扱うのは、 実は罠がいっぱいなんです。
= NULL
では検索できないし、 COUNT()
でカウントされないし…
この記事を読めば、 NULL値の更新が完璧にマスターできます。
実務でよく使う30パターン以上を すべて網羅しました!
【基本】NULL値を別の値に更新する

最も基本的なUPDATE文
-- NULL値を特定の値に更新
UPDATE テーブル名
SET カラム名 = '新しい値'
WHERE カラム名 IS NULL;
-- 実例:従業員テーブルの部署がNULLの場合、'未配属'に更新
UPDATE employees
SET department = '未配属'
WHERE department IS NULL;
-- 複数カラムを同時に更新
UPDATE employees
SET
department = '未配属',
manager_id = 0
WHERE
department IS NULL
OR manager_id IS NULL;
数値型のNULL更新
-- NULLを0に更新
UPDATE products
SET price = 0
WHERE price IS NULL;
-- NULLをデフォルト値に更新
UPDATE products
SET
price = 1000,
stock = 0,
discount = 0
WHERE
price IS NULL
OR stock IS NULL
OR discount IS NULL;
文字列型のNULL更新
-- NULLを空文字に更新
UPDATE users
SET comment = ''
WHERE comment IS NULL;
-- NULLを'不明'に更新
UPDATE users
SET
phone = '未登録',
address = '未登録'
WHERE
phone IS NULL
OR address IS NULL;
【逆パターン】値をNULLに更新する
特定の値をNULLに変更
-- 空文字をNULLに更新
UPDATE users
SET comment = NULL
WHERE comment = '';
-- 0をNULLに更新
UPDATE products
SET price = NULL
WHERE price = 0;
-- 複数条件でNULLに更新
UPDATE employees
SET department = NULL
WHERE
department = '退職'
OR department = '削除'
OR department = '';
NULLIF関数を使った更新
-- 空文字や特定の値をNULLに変換
UPDATE users
SET
-- 空文字の場合NULLに
phone = NULLIF(phone, ''),
-- '未設定'の場合NULLに
address = NULLIF(address, '未設定'),
-- 0の場合NULLに
age = NULLIF(age, 0);
-- TRIMしてから空文字チェック
UPDATE users
SET comment = NULLIF(TRIM(comment), '');
COALESCE関数を使った高度な更新
COALESCEで初期値を設定
-- NULLの場合はデフォルト値、そうでなければ既存値を保持
UPDATE products
SET price = COALESCE(price, 1000);
-- 複数のフォールバック値
UPDATE users
SET
display_name = COALESCE(nickname, username, email, 'ゲスト');
-- 計算を含む更新
UPDATE orders
SET
total = COALESCE(subtotal, 0) + COALESCE(tax, 0) + COALESCE(shipping, 0);
他のカラムの値で補完
-- 別カラムの値でNULLを補完
UPDATE employees
SET
work_email = COALESCE(work_email, personal_email),
emergency_contact = COALESCE(emergency_contact, phone_number);
-- JOINした別テーブルの値で補完
UPDATE products p
SET category_name = COALESCE(p.category_name, c.name)
FROM categories c
WHERE p.category_id = c.id;
CASE文を使った条件付き更新
複雑な条件でのNULL処理
-- NULL値を条件に応じて異なる値に更新
UPDATE employees
SET salary =
CASE
WHEN salary IS NULL AND department = '営業' THEN 300000
WHEN salary IS NULL AND department = '開発' THEN 400000
WHEN salary IS NULL AND department = '管理' THEN 350000
WHEN salary IS NULL THEN 250000 -- デフォルト
ELSE salary -- NULL以外はそのまま
END;
-- 複数カラムの組み合わせ条件
UPDATE products
SET status =
CASE
WHEN price IS NULL AND stock IS NULL THEN '情報不足'
WHEN price IS NULL THEN '価格未設定'
WHEN stock IS NULL THEN '在庫不明'
WHEN stock = 0 THEN '在庫切れ'
ELSE '販売中'
END;
段階的な値の設定
-- NULLと他の条件を組み合わせた更新
UPDATE users
SET user_level =
CASE
WHEN last_login IS NULL THEN '未ログイン'
WHEN last_login < CURRENT_DATE - INTERVAL '1 year' THEN '休眠'
WHEN last_login < CURRENT_DATE - INTERVAL '3 months' THEN '低活性'
WHEN last_login < CURRENT_DATE - INTERVAL '1 month' THEN '通常'
ELSE 'アクティブ'
END;
サブクエリを使ったNULL値の更新
他テーブルの値で更新
-- 別テーブルの値でNULLを更新
UPDATE orders o
SET customer_name = (
SELECT c.name
FROM customers c
WHERE c.id = o.customer_id
)
WHERE o.customer_name IS NULL;
-- 集計値で更新
UPDATE categories
SET product_count = (
SELECT COUNT(*)
FROM products
WHERE products.category_id = categories.id
)
WHERE product_count IS NULL;
EXISTS句との組み合わせ
-- 関連データが存在する場合のみ更新
UPDATE users u
SET has_orders = TRUE
WHERE has_orders IS NULL
AND EXISTS (
SELECT 1 FROM orders o
WHERE o.user_id = u.id
);
-- 関連データがない場合にNULLを設定
UPDATE products p
SET category_name = NULL
WHERE NOT EXISTS (
SELECT 1 FROM categories c
WHERE c.id = p.category_id
);
JSON/JSONB型のNULL処理

JSONフィールドのNULL更新
-- JSON内の特定キーをNULLから値に更新
UPDATE users
SET profile = jsonb_set(
COALESCE(profile, '{}'::jsonb),
'{age}',
'25'
)
WHERE profile->>'age' IS NULL;
-- JSON内のNULL値を削除
UPDATE users
SET profile = profile - 'phone'
WHERE profile->>'phone' IS NULL;
-- 複数のJSONキーを更新
UPDATE users
SET profile =
CASE
WHEN profile IS NULL THEN
'{"name": "未設定", "age": 0}'::jsonb
ELSE
jsonb_set(
jsonb_set(profile, '{name}',
COALESCE(profile->>'name', '"未設定"')::jsonb),
'{age}',
COALESCE(profile->>'age', '0')::jsonb
)
END;
配列型のNULL処理
配列のNULL値更新
-- NULL配列を空配列に更新
UPDATE products
SET tags = ARRAY[]::text[]
WHERE tags IS NULL;
-- 配列内のNULL要素を除去
UPDATE products
SET tags = array_remove(tags, NULL);
-- 配列にデフォルト値を追加
UPDATE products
SET tags = COALESCE(tags, ARRAY[]::text[]) || ARRAY['新商品']
WHERE created_at > CURRENT_DATE - INTERVAL '7 days';
一括更新の最適化テクニック
大量データの効率的な更新
-- バッチ処理で更新(メモリ効率が良い)
DO $$
DECLARE
batch_size INT := 1000;
affected_rows INT;
BEGIN
LOOP
UPDATE products
SET price = 0
WHERE price IS NULL
LIMIT batch_size;
GET DIAGNOSTICS affected_rows = ROW_COUNT;
IF affected_rows = 0 THEN
EXIT;
END IF;
RAISE NOTICE 'Updated % rows', affected_rows;
COMMIT;
END LOOP;
END $$;
インデックスを活用した高速更新
-- NULL値用の部分インデックスを作成
CREATE INDEX idx_null_prices
ON products(id)
WHERE price IS NULL;
-- インデックスを使った更新
UPDATE products
SET price = 1000
WHERE price IS NULL;
-- 更新後、インデックスを削除
DROP INDEX idx_null_prices;
トリガーを使った自動NULL処理
INSERT/UPDATE時の自動補完
-- NULL値を自動的に初期値に変換するトリガー
CREATE OR REPLACE FUNCTION handle_null_values()
RETURNS TRIGGER AS $$
BEGIN
-- NULLの場合、デフォルト値を設定
NEW.price = COALESCE(NEW.price, 0);
NEW.stock = COALESCE(NEW.stock, 0);
NEW.status = COALESCE(NEW.status, 'inactive');
NEW.created_at = COALESCE(NEW.created_at, CURRENT_TIMESTAMP);
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER before_insert_update_products
BEFORE INSERT OR UPDATE ON products
FOR EACH ROW
EXECUTE FUNCTION handle_null_values();
よくあるエラーと対処法
エラー1:= NULLで比較してしまう
-- 間違い
UPDATE users SET status = 'inactive' WHERE last_login = NULL;
-- 0行更新される!
-- 正しい
UPDATE users SET status = 'inactive' WHERE last_login IS NULL;
エラー2:NOT NULLカラムにNULLを設定
-- エラーになる
UPDATE users SET email = NULL WHERE id = 1;
-- ERROR: null value in column "email" violates not-null constraint
-- 対処法1:制約を一時的に削除
ALTER TABLE users ALTER COLUMN email DROP NOT NULL;
UPDATE users SET email = NULL WHERE id = 1;
ALTER TABLE users ALTER COLUMN email SET NOT NULL;
-- 対処法2:デフォルト値を使用
UPDATE users SET email = 'deleted@example.com' WHERE id = 1;
エラー3:NULLの連結
-- 間違い(結果がNULLになる)
UPDATE users
SET full_name = first_name || ' ' || last_name;
-- last_nameがNULLだと、full_nameもNULL!
-- 正しい
UPDATE users
SET full_name = CONCAT(
COALESCE(first_name, ''),
' ',
COALESCE(last_name, '')
);
-- またはCONCAT_WS(区切り文字付き連結)
UPDATE users
SET full_name = CONCAT_WS(' ', first_name, last_name);
パフォーマンス最適化
NULL更新の統計情報
-- 更新前にNULL値の数を確認
SELECT
COUNT(*) FILTER (WHERE price IS NULL) as null_prices,
COUNT(*) FILTER (WHERE price IS NOT NULL) as non_null_prices,
COUNT(*) as total
FROM products;
-- 実行計画の確認
EXPLAIN (ANALYZE, BUFFERS)
UPDATE products SET price = 0 WHERE price IS NULL;
並列更新の活用
-- PostgreSQL 11以降で並列更新を有効化
SET max_parallel_workers_per_gather = 4;
UPDATE products
SET price = COALESCE(price, 0)
WHERE id IN (
SELECT id FROM products
WHERE price IS NULL
);
デバッグとログ

更新内容の事前確認
-- 更新対象を先に確認
BEGIN;
-- 更新対象の確認
SELECT id, price, 'will be 0' as new_value
FROM products
WHERE price IS NULL
LIMIT 10;
-- 実際の更新
UPDATE products
SET price = 0
WHERE price IS NULL
RETURNING id, price;
-- 問題なければコミット、問題があればロールバック
COMMIT; -- または ROLLBACK;
まとめ:状況別NULL更新チートシート
NULLを値に更新:
UPDATE table SET col = value WHERE col IS NULL;
値をNULLに更新:
UPDATE table SET col = NULL WHERE condition;
COALESCEで安全に更新:
UPDATE table SET col = COALESCE(col, default_value);
CASE文で条件分岐:
UPDATE table SET col = CASE WHEN col IS NULL THEN value ELSE col END;
サブクエリで補完:
UPDATE t1 SET col = (SELECT col FROM t2 WHERE t2.id = t1.id) WHERE col IS NULL;
NULL値の扱いをマスターすれば、 データの品質が格段に向上します。
適切なNULL処理で、 クリーンなデータベースを維持しましょう!
NULL処理で困ったことがあれば、ぜひ質問してください。みんなで解決策を共有しましょう!
コメント