SQL

SQLのUPDATEで複数行を一括更新する手順

k.w
\お買い物マラソン開催中/
Contents
  1. まず結論:複数行UPDATEは「対象確認→安全実行→検証」がセット
  2. SQLで複数行をUPDATEする基本構文
  3. WHERE条件で複数行をUPDATEするSQL例
  4. 複数条件でUPDATEする方法(AND / OR)— 括弧ミス事故を防ぐ
  5. IN句で複数IDをまとめてUPDATE(直書き vs サブクエリ)
  6. CASEで複数行を異なる値でUPDATE(ELSE必須+WHEREで限定)
  7. UPDATE時の注意点(WHEREなしは危険)+復旧策
  8. よくある質問(Q & A)(3問に絞る)
  9. まとめ
スポンサーリンク

まず結論:複数行UPDATEは「対象確認→安全実行→検証」がセット

複数行のUPDATEは、SQLの書き方そのものより「どう実行するか」の手順が結果を左右します。

特に本番データに対しては、更新対象の見落としや条件ミスがそのまま事故につながるため、やることを固定し、毎回同じ順番で確認するのが最短です。

このとき重要なのは、UPDATEを“いきなり実行しない”ことです。

まず対象を見て、戻せる状態で実行し、最後に狙いどおりだったかを検証します。

ここで紹介する型(対象確認→安全実行→検証)を守れば、複数行でも落ち着いて更新できます。

事故防止の最短手順(SELECT→BEGIN→UPDATE→件数確認→COMMIT/ROLLBACK)

最初に、UPDATEに書く予定のWHEREをそのままSELECTに移し、更新対象が想定どおりかを「行の中身」と「件数」で確認します。

ここで確認したいのは、単なる件数だけではありません。

たとえば「想定外のステータスが混ざっていないか」「境界値(当日を含める/含めない)が合っているか」「NULLの扱いで拾いすぎていないか」など、事故に直結するポイントを先に潰します。

この段階で、想定より多い・少ない、あるいは混ざってはいけない行が含まれている場合は、UPDATEを打たずに条件を見直します。

次にトランザクションを開始して、戻せる状態でUPDATEを実行します(できる環境なら本番でも基本はこの形が安全です)。

実行直後に影響行数を確認し、さらに再SELECTで差分を確認して、狙った行だけが変わったことを確かめます。

ここで「対象外が変わっていないこと」も一緒に見ると、条件ミスに気づきやすくなります。

問題なければCOMMITして確定します。

少しでも違和感があればROLLBACKして、条件やSET内容、AND/ORの括弧、境界値(< / <= など)、さらには文字の大小や前後空白なども含めて見直し、やり直します。

以下は流れをそのまま写せるテンプレです(テーブル名・列名・条件は置き換えて使います)。

— 1) 対象確認(まずは同じWHEREで確認)

SELECT *

FROM users

WHERE status = ‘pending’;

— 2) 安全実行(DBによりBEGIN/START TRANSACTIONなど)

BEGIN;

— 3) 更新

UPDATE users

SET status = ‘active’

WHERE status = ‘pending’;

— 4) 検証(影響行数+再SELECT)

SELECT *

FROM users

WHERE status = ‘active’;

— 5) 確定 or 取り消し

COMMIT;

— ROLLBACK;

補足として、更新前に「対象の主キーだけ」や「更新列だけ」を控えておくと、検証が速くなります。

さらに安全にするなら、更新後に“控えた主キーだけ”で再SELECTして、意図した行が確実に変わっているかを確認するとブレません。

SELECT id

FROM users

WHERE status = ‘pending’

ORDER BY id;

DB方言の注意(MySQL/PostgreSQL/SQL Serverの差分が出やすい所)

UPDATE自体は共通ですが、トランザクションの開始方法(BEGIN / START TRANSACTION など)や、別テーブルの情報を参照しながら更新するJOIN更新で方言が出ます。

特に「別テーブル参照してUPDATEしたい」場合は、PostgreSQLやSQL ServerでUPDATE FROM、MySQLでUPDATE JOINのように書き分けが必要です。

また、日付型のリテラル表記(DATE ‘YYYY-MM-DD’)やINTERVALの書き方、CURRENT_TIMESTAMP/CURRENT_DATEの扱いもDBにより微妙に異なることがあります。

環境差があると「動かない」だけでなく「動くけど意図と違う」になり得るため、実行前に対象確認(SELECT)を徹底するのが最終的な保険になります。

本記事は基本として標準的なUPDATEを中心に説明し、方言が出る箇所は「注意点」として補足します。

SQLで複数行をUPDATEする基本構文

複数行更新は「SETで何を変えるか」と「WHEREでどこまで絞るか」の2点で決まります。

言い換えると、SETは“変更内容”、WHEREは“影響範囲”です。

UPDATEの基本形(SET/WHERE)

UPDATEは指定したテーブルの列を更新する命令です。

SETで更新後の値(上書きする内容)を指定します。

WHEREで更新対象の行を絞り込みます。

基本形は次のとおりです。

UPDATE テーブル名

SET 列1 = 値1, 列2 = 値2

WHERE 条件;

WHEREが複数行に一致すれば、その一致した行がまとめて更新されます。

逆にWHEREが1行だけに一致すれば、更新は1行だけになります。

なぜ複数行になる?(条件が複数レコードに一致する仕組み)

WHEREは「この条件に当てはまる行すべて」を対象にします。

たとえばstatusがpendingのユーザーが10人いれば、同じWHEREで10行が更新されます。

逆に主キー(idなど)で1件に絞っていれば、更新は1行だけになります。

つまり複数行になるかどうかは、条件がユニークかどうか(主キー/ユニーク制約で絞れるか)で決まります。

慣れないうちは「このWHEREは何行に当たるか」をUPDATE前に必ず数える癖を付けるのが安全です。

実行前チェック:同じWHEREでSELECTして件数確認

UPDATEを書く前に、同じWHEREをSELECTで試すのが最も効果的な安全策です。

SELECT結果の件数が想定より多いなら、そのUPDATEは危険です。

逆に少なすぎる場合も、条件ミスやデータ前提のズレが疑えます。

件数だけ確認したい場合はCOUNTを使います。

SELECT COUNT(*)

FROM users

WHERE status = ‘pending’;

更新前の状態をあとで比較できるように、必要なら対象行の主キー一覧も控えます。

SELECT id

FROM users

WHERE status = ‘pending’

ORDER BY id;

この「同じWHEREでSELECT→更新後も同じ観点でSELECT」が、複数行UPDATEを安全にする基本動作です。

WHERE条件で複数行をUPDATEするSQL例

一括更新の多くは、同じ値を同じ条件でまとめて更新するパターンです。

まずはこの型を身に付けると、INやCASEなどの応用も理解しやすくなります。

例:ステータス/フラグをまとめて更新

フラグ更新は実務で最も頻出で、複数行UPDATEの入口として最適です。

たとえば未確認ユーザーを一括で確認済みにするなら次のように書きます。

UPDATE users

SET verified = 1

WHERE verified = 0;

同時に更新日時も更新したい場合はSETに列を追加します。

UPDATE users

SET verified = 1, verified_at = CURRENT_TIMESTAMP

WHERE verified = 0;

このように「同じ条件で同じ値を入れる」更新は、まずSELECTで対象確認さえしておけば比較的安全に実行できます。

例:日付・範囲条件で更新

日付や数値の範囲でまとめて更新するケースも多いです。

たとえば一定期間ログインがないユーザーを休眠扱いにする例です。

UPDATE users

SET status = ‘inactive’

WHERE last_login_at < DATE ‘2025-01-01’;

範囲条件は境界(< と <=)を間違えやすいので、UPDATE前に必ずSELECTで確認します。

SELECT id, last_login_at

FROM users

WHERE last_login_at < DATE ‘2025-01-01’

ORDER BY last_login_at;

この確認をすると、「思ったより古いユーザーが多い」「特定の期間だけ異常に多い」などの前提ズレも見つけやすくなります。

更新後検証(再SELECT/差分確認の観点)

更新後は「対象が変わったこと」と「対象外が変わっていないこと」を確認します。

まず同じWHEREで再度SELECTして、件数が0や想定値になっているかを見ます(例:pendingが0件になった、など)。

次に更新した列が意図どおりの値になっているかを、数件サンプルで確認します。

可能なら更新対象の主キー一覧で絞って再確認すると、検証が安定します。

また、必要に応じて「更新前と更新後の件数」をセットで記録すると、作業ログとしても役立ちます。

複数条件でUPDATEする方法(AND / OR)— 括弧ミス事故を防ぐ

条件が増えるほど対象がズレやすくなるので、ANDとORの優先順位を意識します。

特にORは対象が増える方向に働くため、更新範囲が一気に広がる事故が起きがちです。

ANDで絞る(安全に狭める)

ANDは条件を足すほど対象が減るので、安全側に倒すときに使います。

たとえば特定ステータスかつ特定プランのユーザーだけ更新する例です。

UPDATE users

SET status = ‘active’

WHERE status = ‘pending’

AND plan = ‘pro’;

ANDで条件を追加するときは、追加前後でSELECT件数を比較すると安心です。

「追加した条件で何件減ったか」を見るだけでも、意図した絞り込みになっているか判断しやすくなります。

ORで広げる(想定外に増えるリスク)

ORは条件を足すほど対象が増えるので、更新範囲が広がるリスクがあります。

たとえば2つのステータスをまとめて更新する例です。

UPDATE users

SET flagged = 1

WHERE status = ‘suspended’

OR status = ‘banned’;

ORを使うときは、更新対象が増える前提で件数確認を丁寧に行います。

可能ならORの各条件を別々にCOUNTして、合算したときのイメージを持ってからUPDATEすると安全です。

NG→OK:AND/OR混在は括弧が必須

ANDとORを混ぜると、括弧がない場合に意図と違う行が対象になることがあります。

まず事故が起きやすいNG例です。

— NG例:意図は「pending かつ(pro または enterprise)」だが違う結果になりうる

UPDATE users

SET status = ‘active’

WHERE status = ‘pending’

AND plan = ‘pro’

OR plan = ‘enterprise’;

次に意図を固定するOK例です。

— OK例:括弧で条件のまとまりを明確にする

UPDATE users

SET status = ‘active’

WHERE status = ‘pending’

AND (plan = ‘pro’ OR plan = ‘enterprise’);

混在条件は、括弧を付けた上で同じ条件をSELECTに移して結果を確認します。

このとき、意図したグルーピングになっているかを“言葉にして読む”(例:pending かつ(proまたはenterprise))とミスが減ります。

IN句で複数IDをまとめてUPDATE(直書き vs サブクエリ)

INは「この集合に含まれる行だけ」を狙い撃ちできるので、一括更新を安全にしやすい手段です。

WHEREの条件が複雑になりそうなときほど、いったんID集合に落としてから更新すると見通しが良くなります。

IN(ID直書き)の基本例

手元に更新対象のIDが決まっているなら、INでまとめて指定できます。

UPDATE users

SET status = ‘active’

WHERE id IN (101, 205, 330);

ID直書きは早い反面、対象が増えると読みづらくなります。

また、手作業でIDを作る場合は重複や抜けが起きやすいので、可能なら元となる抽出SQLも残しておくと安全です。

IN(サブクエリ)を優先すべき場面

条件で抽出したID群をそのままINに渡すと、再現性が高くなります。

たとえば「直近30日で未払いがあるユーザーだけフラグを立てる」例です。

UPDATE users

SET billing_hold = 1

WHERE id IN (

SELECT user_id

FROM invoices

WHERE status = ‘unpaid’

AND issued_at >= CURRENT_DATE – INTERVAL ’30 day’

);

サブクエリを使う場合でも、まずサブクエリ単体をSELECTとして実行して対象IDを確認します。

さらに安全にするなら、サブクエリの結果件数をCOUNTしてからUPDATEに進むと、規模感の取り違えを防げます。

IDが多いときの注意(可読性/運用/パラメータ化)

IDが多い場合は、アプリ側でパラメータ化して渡すか、一時テーブルにIDを入れてJOIN更新を検討します。

SQLの可読性が落ちると、レビューや検証で事故が起きやすくなります。

「ID集合を管理する仕組み」を用意すると、更新のたびに手作業で列挙する必要が減ります。

特に定期バッチや運用作業では、ID集合(対象リスト)を作る工程と、更新する工程を分離しておくと、確認がしやすくなります。

CASEで複数行を異なる値でUPDATE(ELSE必須+WHEREで限定)

CASEを使うと、1回のUPDATEで行ごとに違う値を割り当てられます。

ただし強力な分、範囲を間違えると広範囲を上書きできてしまうので、ELSEとWHEREをセットで考えるのが基本です。

CASE更新の基本例

代表例は、IDごとにランクやスコアを割り当てる更新です。

UPDATE users

SET rank = CASE id

WHEN 101 THEN ‘gold’

WHEN 205 THEN ‘silver’

WHEN 330 THEN ‘bronze’

ELSE rank

END

WHERE id IN (101, 205, 330);

このようにCASEはSETの右辺に書き、どの条件で何を入れるかを列挙します。

「ELSEで変更しない」「WHEREで対象を限定する」を同時に入れておくと、想定外の行が混ざっても被害が小さくなります。

ELSEがないと何が起きる?(NULL化など)+ELSEの書き方

ELSEを書かないと、どのWHENにも当てはまらない行がNULLになる方言や設定があり得ます。

特に列がNULLを許容している場合、気づきにくい形でデータが壊れることがあります。

安全のために、基本はELSEで「変更しない」か「明示値」を入れます。

UPDATE users

SET score = CASE

WHEN country = ‘JP’ THEN 10

WHEN country = ‘US’ THEN 8

ELSE score

END;

変更しない場合はELSEに同じ列を戻すのが分かりやすいです。

一方で「当てはまらない行は必ずエラーにしたい」というケースは、先にSELECTで当てはまらない行が存在しないことを確認してからUPDATEします。

WHEREで対象を限定する

CASEは便利ですが、WHEREを付けないとテーブル全体に評価が走ります。

意図した範囲だけに適用するために、CASEとWHEREをセットにします。

UPDATE users

SET tier = CASE

WHEN spend >= 100000 THEN ‘A’

WHEN spend >= 50000 THEN ‘B’

ELSE ‘C’

END

WHERE status = ‘active’;

対象が多い更新ほど、WHEREで限定してから段階的に適用します。

たとえば最初は1日分だけ、あるいは特定のIDレンジだけに当てて、結果を見ながら広げると安全です。

分岐が多いときの代替(JOIN更新/一時テーブル/更新用マスタ)

WHENが増えすぎるとSQLが読めなくなり、保守と検証が難しくなります。

その場合は「更新用の対応表」をテーブル化してJOIN更新する方が安全です。

また一時テーブルにIDと更新値を入れてから更新すると、レビューしやすい形にできます。

“更新する値の根拠”をテーブルとして残せるので、後から追跡したい業務でも有効です。

UPDATE時の注意点(WHEREなしは危険)+復旧策

UPDATEは一度確定すると戻せないことがあるので、実行前後のガードを仕組みにします。

ここは知識というより運用で、ルール化できるほど安全になります。

WHEREなし更新が起きる原因

WHEREを書き忘れるミスが最も多い原因です。

WHEREがあっても条件が緩すぎて全件に当たるケースもあります。

さらにAND/ORの括弧ミスで対象が広がる事故も頻発します。

加えて、コピー&ペーストで別のSQLのWHEREを誤って持ってきてしまうミスも多いので、実行前のSELECT確認が効きます。

トランザクションで安全に試す(COMMIT/ROLLBACK)

更新は可能な限りトランザクション内で実行します。

更新後にSELECTで検証してからCOMMITすれば、違和感があればROLLBACKで戻せます。

BEGIN;

UPDATE users

SET status = ‘inactive’

WHERE last_login_at < DATE ‘2025-01-01’;

SELECT COUNT(*)

FROM users

WHERE last_login_at < DATE ‘2025-01-01’

AND status = ‘inactive’;

— 問題なければ

COMMIT;

— 問題があれば

— ROLLBACK;

運用上は「検証SQLをセットで用意する」ことが最も効きます。

たとえば“更新前件数/更新後件数/想定外が混ざっていないか”を1セットにしておくと、作業が安定します。

復旧の考え方(更新前SELECT保存/バックアップ/ログの取り方)

万一に備えて、更新前に対象行の主キーと更新対象列を控えるのが現実的です。

更新対象が小さいなら、更新前SELECTの結果をエクスポートして差分を追えるようにします。

本番ではバックアップと監査ログが前提になるので、更新系作業のルールに組み込みます。

加えて「更新した人・時刻・理由・SQL」を最低限残すだけでも、トラブル時の復旧判断が速くなります。

本番チェックリスト(実行前・実行直後)

実行前は「同じWHEREでSELECTして件数確認」を必ず行います。

実行中はトランザクションを使い、検証が終わるまでCOMMITしません。

実行直後は影響行数と再SELECTを確認し、想定外があれば即ROLLBACKします。

チーム運用なら、更新SQLはレビュー(もう一人がWHEREと括弧を確認)を挟むだけでも事故率が下がります。

よくある質問(Q & A)(3問に絞る)

最後に、複数行UPDATEでよく詰まるポイントを短く整理します。

ここだけ先に読んでも、最低限の不安が解消できるようにまとめます。

更新件数(影響行数)を確認する方法は?

多くのDBやクライアントはUPDATE実行後に影響行数を表示します。

確実に把握したい場合は、UPDATE前にCOUNTで件数を取ります。

更新後は同じ条件で再度COUNTし、想定どおり変化したか確認します。

さらに安全にしたい場合は、主キー一覧を控えておき、更新後にそのIDだけ再確認すると検証がブレません。

更新を“安全に戻す”には?

最も安全なのはトランザクション内で実行して、検証後にCOMMITする運用です。

COMMIT後に戻す必要がある場合は、バックアップや監査ログがないと復旧が難しくなります。

更新前のSELECT結果を控える運用は、復旧の手がかりとして有効です。

「戻すSQL(元の値に戻すUPDATE)」を作るには、元データが必須なので、更新前の控えが効いてきます。

CASE更新で漏れが出るのはなぜ?

WHENに当たらない行が想定より多いと、更新漏れや意図しない値になります。

ELSEで「変更しない」か「デフォルト」を明示すると漏れを検知しやすいです。

加えてWHEREで対象を限定し、段階的に適用すると原因切り分けが簡単です。

漏れを見つけたいときは、WHEN条件に当たらない行だけをSELECTで抽出して確認すると早いです。

まとめ

複数行UPDATEは「同じWHEREでSELECT確認」を起点にすると安全性が一気に上がります。

次にINで狙いどおりに絞り、CASEはELSEとWHEREをセットにして事故を防ぎます。

最後にトランザクションと検証SQLをルール化すれば、複数行更新でも怖くなくなります。

迷ったら、更新そのものを工夫するよりも、更新前後の確認を増やす方が確実です。

スポンサーリンク
記事URLをコピーしました