本番DBでWHEREなしUPDATEを実行した恐怖体験と事故防止策
概要¶
検証環境のデータをクライアントツールで眺めていたとき、軽い気持ちで UPDATE を実行したら WHERE 句を書き忘れて全件更新してしまった(と思われる)体験談。DB の怖さを身をもって知るエピソード。
詳細¶
インシデントの全貌¶
よくある事故のシナリオ:
- 検証環境の DB に接続して、テーブルの中身を確認していた
- 特定のレコードだけ status を変えたくて、UPDATE 文を書き始めた
- WHERE 句を書く前に、うっかり実行してしまった(または WHERE 句を書いたが条件が甘かった)
- テーブルの全レコードの status が書き変わった
-- やりたかったこと
UPDATE users SET status = 'inactive' WHERE id = 123;
-- 実際にやってしまったこと
UPDATE users SET status = 'inactive';
-- → users テーブルの全件が status = 'inactive' になった
検証環境だったとしても、テストデータの状態が壊れてチームの作業が止まったり、本番環境で同じことをやったらと考えると背筋が凍る体験。
なぜこの事故が起きやすいのか¶
GUIツールの罠
TablePlus・DBeaver・Sequel Pro などの DB クライアントツールは「データを見る」「クエリを実行する」がシームレスにできる。これが便利である反面、「軽い気持ちで実行できてしまう」環境を作り出している。
ターミナルから psql や mysql でアクセスする場合は、コマンドを打つ心理的コストが多少あるが、GUI ツールでは「テーブルを開いてクエリを書いて Enter を押す」という流れが自然にできてしまう。
確認ダイアログがない
ほとんどの DB クライアントツールは「本当に実行しますか?」というダイアログを出さない。削除・更新系の操作も即時実行される。
WHERE 句を後で書くつもりだった
「とりあえず UPDATE 文の骨格を書いて、WHERE は後で書こう」と思って途中で実行してしまうパターン。また「WHERE id = 」まで書いたがその後に数字を入れる前に Enter を押してしまうケースも。
DB操作の安全プラクティス¶
ルール1: UPDATE/DELETE の前に必ず SELECT で確認
-- まず影響範囲を SELECT で確認する
SELECT * FROM users WHERE id = 123;
-- 期待通りのレコードが返ってきたことを確認
-- 確認できたら UPDATE を実行
UPDATE users SET status = 'inactive' WHERE id = 123;
「SELECT してから UPDATE」の習慣を身につけることで、WHERE 句の条件が正しいかを先に確認できる。
ルール2: WHERE 句なしの UPDATE/DELETE は絶対に書かない
-- 危険: WHERE 句なし(全件更新)
UPDATE users SET status = 'inactive';
-- 安全: 必ず WHERE 句をつける
UPDATE users SET status = 'inactive' WHERE id = 123;
-- さらに安全: 影響件数を事前に確認
SELECT COUNT(*) FROM users WHERE id = 123;
-- → 1件であることを確認してから UPDATE
「WHERE なし」は意図的に全件操作するときだけ書く。誤って全件操作する事故のほとんどは WHERE 句の書き忘れが原因。
ルール3: トランザクションで囲む
-- トランザクションを使えば、間違えてもロールバックできる
BEGIN;
UPDATE users SET status = 'inactive' WHERE id = 123;
-- 結果を確認
SELECT * FROM users WHERE id = 123;
-- ← 意図通りなら COMMIT、そうでなければ ROLLBACK
COMMIT;
-- または
ROLLBACK;
PostgreSQL・MySQL ともに BEGIN でトランザクションを開始できる。COMMIT を実行するまで変更は確定しないため、間違えた場合に ROLLBACK で元に戻せる。
ルール4: 本番・検証環境の接続を明確に区別する
接続先を視覚的に区別することで「どの環境に接続しているか」を常に意識できる。本番環境への接続は、必要なとき以外は切断しておくのが安全。
ルール5: LIMIT を使って影響範囲を制限する
-- 更新件数を制限して安全に実行
UPDATE users SET status = 'inactive'
WHERE created_at < '2020-01-01'
LIMIT 100;
-- → 万が一 WHERE 条件が間違っていても最大 100 件しか更新されない
大量更新が必要な場合は LIMIT で小さく分けて実行する。全件更新が意図通りであっても、一度に大量の行を更新するとテーブルロックが発生してサービスに影響する場合がある。
「ちょっと見るだけ」が事故を生む心理¶
「検証環境だから」「データを見るだけだから」という心理的な安心感が油断を生む。
- 見るだけのつもりが実行してしまう
- 検証環境だからと雑に操作して本番と同じミスを本番でやる
- 「大丈夫だろう」という慢心が判断を鈍らせる
DB 操作に関しては「本番環境で同じ操作をするとどうなるか」を常に意識する習慣が重要。
事故が起きたときの対応¶
- すぐに接続を切らない(ログを残すために接続は維持する)
- トランザクション中なら即座に
ROLLBACK - バックアップからのリストアを検討する(検証環境は定期バックアップがない場合も多い)
- 影響範囲を特定する(何件更新されたか、何のデータが壊れたか)
- チームに報告する(隠しても状況は改善しない)
なぜ重要か / いつ使うか¶
- DB クライアントツールを初めて使い始めたエンジニアへの警告として
- 「検証環境だから何でもやっていい」という油断を持っているチームへの教訓として
- 本番に近い環境(ステージング・UAT)での DB 操作ルールを決めるとき
- インシデント対応の訓練として「もし全件更新してしまったらどうするか」を事前に考えるとき
- チームの DB 操作ガイドラインを作るときの叩き台として