MySQLでUPDATEを複数行まとめて一括更新する方法|パフォーマンス重視のSQLテクニック

データベース・SQL

「IDごとに別々の値を一気に更新したい」
「大量データをループせずに高速で処理したい」
「1件ずつ更新するのは時間がかかりすぎる」
そんなときに便利なのが、MySQLのUPDATE複数行一括更新です。

「複数行の更新って難しそう…」
「どんな書き方をすればいいの?」
そんな疑問をお持ちの方も多いでしょう。

実は、いくつかのパターンを覚えれば、効率的にデータを更新できるようになります。

この記事では、MySQLで複数行の異なる値を一度に更新する方法と、効率的な実行パターン、注意点を初心者向けにわかりやすく解説します。

スポンサーリンク

通常のUPDATE文の限界と問題点

一般的なUPDATE文の書き方

基本的な構文

UPDATE users
SET age = 30
WHERE id = 1;

この方法の問題点
通常のUPDATE文は、一つの条件に対して一つの値しか設定できません。
つまり、複数の行に異なる値を設定したい場合は、何回もSQLを実行する必要があります。

非効率なパターンの例

悪い例:繰り返し実行

UPDATE users SET age = 25 WHERE id = 1;
UPDATE users SET age = 30 WHERE id = 2;
UPDATE users SET age = 28 WHERE id = 3;
UPDATE users SET age = 35 WHERE id = 4;
-- ...さらに続く

この方法の問題

  • パフォーマンスが悪い:データベースへの接続回数が多い
  • 時間がかかる:数百件を更新する場合、非現実的
  • トランザクション管理が複雑:途中でエラーが起きた時の処理が大変
  • ネットワーク負荷:何度もクエリを送信するため負荷が高い

これらの問題を解決するのが、一括更新のテクニックです。

CASE文+IN句で一括更新する方法【推奨】

基本構文の説明

構文パターン

UPDATE テーブル名
SET カラム名 = CASE 主キー
  WHEN 値1 THEN 更新値1
  WHEN 値2 THEN 更新値2
  WHEN 値3 THEN 更新値3
  ...
END
WHERE 主キー IN (値1, 値2, 値3, ...);

構文の各部分説明

  • CASE 主キー:条件分岐の基準となる列を指定
  • WHEN 値 THEN 更新値:「もし主キーが○○なら××に更新」という条件
  • WHERE 主キー IN (...):更新対象の行を限定(安全性のため重要)

実用的な例:ユーザー年齢の一括更新

実例

UPDATE users
SET age = CASE id
  WHEN 1 THEN 25
  WHEN 2 THEN 30
  WHEN 3 THEN 28
  WHEN 4 THEN 35
  WHEN 5 THEN 22
END
WHERE id IN (1, 2, 3, 4, 5);

実行結果

  • id=1のユーザーは25歳に更新
  • id=2のユーザーは30歳に更新
  • id=3のユーザーは28歳に更新
  • id=4のユーザーは35歳に更新
  • id=5のユーザーは22歳に更新

なぜこの方法が効率的なのか?

  1. 1回のSQL実行で複数行を更新
  2. データベースへの接続は1回だけ
  3. トランザクション管理が簡単
  4. 処理速度が大幅に向上

複数カラムを同時に更新する方法

複数カラム更新の基本構文

構文パターン

UPDATE テーブル名
SET
  カラム1 = CASE 主キー
    WHEN 値1 THEN 更新値1
    WHEN 値2 THEN 更新値2
    ...
  END,
  カラム2 = CASE 主キー
    WHEN 値1 THEN 更新値1
    WHEN 値2 THEN 更新値2
    ...
  END
WHERE 主キー IN (値1, 値2, ...);

実用例:商品情報の一括更新

商品価格と在庫を同時更新

UPDATE products
SET
  price = CASE id
    WHEN 101 THEN 1000
    WHEN 102 THEN 1200
    WHEN 103 THEN 800
  END,
  stock = CASE id
    WHEN 101 THEN 50
    WHEN 102 THEN 30
    WHEN 103 THEN 75
  END,
  last_updated = CASE id
    WHEN 101 THEN '2024-01-15'
    WHEN 102 THEN '2024-01-15'
    WHEN 103 THEN '2024-01-15'
  END
WHERE id IN (101, 102, 103);

実行結果

idpricestocklast_updated
1011000502024-01-15
1021200302024-01-15
103800752024-01-15

より実践的な例:社員情報の更新

部署異動と昇給を同時処理

UPDATE employees
SET
  department = CASE employee_id
    WHEN 'E001' THEN '営業部'
    WHEN 'E002' THEN '開発部'
    WHEN 'E003' THEN '人事部'
  END,
  salary = CASE employee_id
    WHEN 'E001' THEN 350000
    WHEN 'E002' THEN 420000
    WHEN 'E003' THEN 380000
  END,
  updated_at = NOW()
WHERE employee_id IN ('E001', 'E002', 'E003');

JOINを使って他テーブルから更新【応用】

JOINによる更新の基本構文

基本パターン

UPDATE 更新対象テーブル t
JOIN 参照テーブル s ON t.結合キー = s.結合キー
SET t.更新カラム1 = s.参照カラム1,
    t.更新カラム2 = s.参照カラム2;

実用例:一時テーブルからの一括更新

ケース:CSVインポート後の一括更新

-- 一時テーブルの作成(CSVデータをインポート)
CREATE TEMPORARY TABLE temp_updates (
  user_id INT,
  new_email VARCHAR(255),
  new_phone VARCHAR(20)
);

-- 一時テーブルにデータを挿入
INSERT INTO temp_updates VALUES
(1, 'user1@newdomain.com', '090-1234-5678'),
(2, 'user2@newdomain.com', '090-2345-6789'),
(3, 'user3@newdomain.com', '090-3456-7890');

-- JOINを使った一括更新
UPDATE users u
JOIN temp_updates t ON u.id = t.user_id
SET u.email = t.new_email,
    u.phone = t.new_phone,
    u.updated_at = NOW();

より高度な例:計算結果による更新

注文合計金額の計算と更新

UPDATE orders o
JOIN (
  SELECT 
    order_id,
    SUM(price * quantity) as total_amount
  FROM order_items
  GROUP BY order_id
) calc ON o.id = calc.order_id
SET o.total_amount = calc.total_amount,
    o.updated_at = NOW();

この方法で、注文明細から自動計算した合計金額を注文テーブルに反映できます。

条件付き更新と部分更新

条件に応じた柔軟な更新

特定条件の行のみ更新

UPDATE users
SET status = CASE
  WHEN last_login < DATE_SUB(NOW(), INTERVAL 30 DAY) THEN 'inactive'
  WHEN last_login < DATE_SUB(NOW(), INTERVAL 7 DAY) THEN 'dormant'
  ELSE 'active'
END,
priority = CASE
  WHEN subscription_type = 'premium' THEN 1
  WHEN subscription_type = 'standard' THEN 2
  ELSE 3
END
WHERE id IN (1, 2, 3, 4, 5);

NULL値の条件付き更新

既存値がNULLの場合のみ更新

UPDATE products
SET description = CASE id
  WHEN 101 THEN COALESCE(description, '新商品の説明')
  WHEN 102 THEN COALESCE(description, '人気商品の説明')
  WHEN 103 THEN COALESCE(description, '限定商品の説明')
  ELSE description
END
WHERE id IN (101, 102, 103);

パフォーマンス最適化のコツ

インデックスの活用

WHERE句で使用する列にインデックスを設定

-- 主キーは通常自動でインデックスが設定されているが、
-- 他の列を使う場合は明示的に設定
CREATE INDEX idx_users_email ON users(email);

バッチサイズの調整

大量データを分割して処理

-- 悪い例:一度に数万件を更新
UPDATE users SET ... WHERE id IN (1,2,3,...,50000);

-- 良い例:1000件ずつに分割
UPDATE users SET ... WHERE id IN (1,2,3,...,1000);
-- 少し待ってから次のバッチ
UPDATE users SET ... WHERE id IN (1001,1002,...,2000);

更新前後の確認

安全な更新手順

-- 1. 更新対象を事前確認
SELECT id, age FROM users WHERE id IN (1, 2, 3);

-- 2. トランザクション開始
START TRANSACTION;

-- 3. 更新実行
UPDATE users
SET age = CASE id
  WHEN 1 THEN 25
  WHEN 2 THEN 30
  WHEN 3 THEN 28
END
WHERE id IN (1, 2, 3);

-- 4. 結果確認
SELECT id, age FROM users WHERE id IN (1, 2, 3);

-- 5. 問題なければコミット、問題があればロールバック
COMMIT; -- または ROLLBACK;

注意点とベストプラクティス

重要な注意点

注意点内容対策
WHERE句の省略条件なしで実行すると全レコードが更新される必ずWHERE句を記述する
CASEのWHEN重複主キーが重複すると予期しない結果になる重複チェックを事前に実行
大量データ処理一度に処理すると時間がかかりすぎるバッチサイズを適切に設定
トランザクション途中でエラーが起きた時の対応トランザクションを使用

安全な実行のためのチェックリスト

実行前の確認事項

  1. バックアップの取得:重要なデータは事前にバックアップ
  2. テスト環境での検証:本番環境での実行前に必ずテスト
  3. 影響範囲の確認:どの行が更新されるかを事前に確認
  4. 処理時間の見積もり:大量データの場合は処理時間を計算

トランザクション管理の例

安全な実行パターン

-- トランザクション開始
START TRANSACTION;

-- 更新実行
UPDATE users
SET age = CASE id
  WHEN 1 THEN 25
  WHEN 2 THEN 30
  WHEN 3 THEN 28
END
WHERE id IN (1, 2, 3);

-- 影響を受けた行数を確認
SELECT ROW_COUNT() as affected_rows;

-- 結果が期待通りなら確定、そうでなければ取り消し
-- COMMIT; または ROLLBACK;

実践的な活用例

ECサイトでの在庫管理

複数商品の在庫を注文に応じて減算

UPDATE products
SET stock = CASE id
  WHEN 101 THEN stock - 2  -- 商品101を2個減算
  WHEN 102 THEN stock - 1  -- 商品102を1個減算
  WHEN 103 THEN stock - 3  -- 商品103を3個減算
END,
last_updated = NOW()
WHERE id IN (101, 102, 103)
  AND stock >= CASE id        -- 在庫不足チェック
    WHEN 101 THEN 2
    WHEN 102 THEN 1
    WHEN 103 THEN 3
  END;

ユーザーのランク更新

購入金額に応じたランク自動更新

UPDATE users u
JOIN (
  SELECT 
    user_id,
    SUM(total_amount) as total_purchased
  FROM orders
  WHERE created_at >= DATE_SUB(NOW(), INTERVAL 1 YEAR)
  GROUP BY user_id
) stats ON u.id = stats.user_id
SET u.rank = CASE
  WHEN stats.total_purchased >= 100000 THEN 'GOLD'
  WHEN stats.total_purchased >= 50000 THEN 'SILVER'
  WHEN stats.total_purchased >= 10000 THEN 'BRONZE'
  ELSE 'REGULAR'
END,
u.rank_updated_at = NOW();

まとめ

MySQLでの複数行一括UPDATEは、CASE文を使った条件指定が基本であり、大幅なパフォーマンス向上を実現できます。

今回のポイントをおさらい

パターン使用場面メリット
CASE文による更新異なる値に個別更新シンプルで理解しやすい
複数カラム同時更新関連する複数項目を一括変更整合性を保ちながら効率的
JOINによる更新他テーブルの値を参照柔軟で高度な更新が可能

今日からできること

  1. 現在の更新処理をCASE文に置き換えてみる
  2. 複数回実行していたUPDATEを一括化する
  3. トランザクションを使った安全な更新を実践
  4. 処理時間を測定してパフォーマンス向上を確認

コメント

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