PostgreSQL NULL値UPDATE完全ガイド!NULLの更新・置換・処理のすべて

データベース・SQL

「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処理で困ったことがあれば、ぜひ質問してください。みんなで解決策を共有しましょう!

コメント

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