Day16 後半
「“一括で直す”UPDATE」と「絶対に壊さないための考え方」
前半で、UPDATE の基本
「どの行の」「どの列を」「どう書き換えるか」
は押さえました。
後半では、もう一歩踏み込んで、
- 条件を工夫した“まとめて更新”
- フラグ更新(状態を切り替える)
- サブクエリを使った UPDATE の考え方
- 「安全に UPDATE するためのチェック習慣」
を扱っていきます。
ここから先は、“強い力をどうコントロールするか” の話です。
状態フラグを UPDATE で切り替える
「active / inactive」「削除フラグ」を変える
実務で一番よく出てくる UPDATE は、
「状態フラグを切り替える」タイプです。
たとえば、users テーブルに is_active 列があるとします。
id | name | is_active
---+------------+----------
1 | 山田太郎 | 1
2 | 佐藤花子 | 1
3 | 鈴木一郎 | 1
ここで、「id=2 のユーザーを“無効化”したい」とします。
UPDATE users
SET is_active = 0
WHERE id = 2;
SQLこれで、id=2 だけが「アクティブ → 非アクティブ」に変わります。
さらに、「30歳以上のユーザーを一括で非アクティブにする」なら、こうです。
UPDATE users
SET is_active = 0
WHERE age >= 30;
SQLここで重要なのは、
「削除」ではなく「状態を変える」ことで、
“いつでも戻せる”余地を残している という点です。
セキュリティ的にも、
「物理削除(DELETE)ではなく論理削除(フラグ更新)」
はよく使われるパターンです。
日付や条件を使った“期間ベースの更新”
「一定期間ログインしていないユーザーを休眠扱いにする」
もう少し実務寄りの例にします。
users に last_login_at と status があるとします。
id | name | status | last_login_at
---+------------+----------+-------------------
1 | 山田太郎 | active | 2025-04-01
2 | 佐藤花子 | active | 2023-01-01
3 | 鈴木一郎 | active | 2022-12-31
ここで、
「1年以上ログインしていないユーザーを status=’inactive’ にしたい」
という要件を考えます。
SQLite では日付関数を使って、こう書けます。
UPDATE users
SET status = 'inactive'
WHERE last_login_at < date('now', '-1 year');
SQLここでの流れは、
date('now', '-1 year') で「1年前の日付」を求める
last_login_at がそれより古いユーザーだけを対象にする
status を ‘inactive’ に更新する
というものです。
このように、
「時間 × 状態フラグ × UPDATE」
は、運用・セキュリティの両面でよく出てくるパターンです。
サブクエリを使った UPDATE の考え方
「別テーブルの情報をもとに、一括で値を更新する」
SQLite は一部の UPDATE JOIN が制限されますが、
考え方として「サブクエリで値を決めてから UPDATE する」パターンは重要です。
たとえば、users と user_profiles があり、user_profiles にだけ正しい年齢が入っているとします。
users
id | name | age
---+------------+----
1 | 山田太郎 | NULL
2 | 佐藤花子 | NULL
user_profiles
user_id | real_age
--------+---------
1 | 25
2 | 19
「users.age を user_profiles.real_age で一括更新したい」とします。
SQLite では、JOIN を直接 UPDATE に書くのは難しいので、
まず SELECT で「どう更新されるべきか」を確認するのが鉄則です。
SELECT
u.id,
u.name,
p.real_age
FROM users AS u
INNER JOIN user_profiles AS p
ON u.id = p.user_id;
SQLこの結果を確認してから、
実際の UPDATE はアプリ側でループして行う、という設計もよくあります。
ここで大事なのは、
「まず SELECT で“更新後の姿”を確認してから、UPDATE を設計する」
という思考パターンです。
SQLite 単体での複雑な UPDATE は無理にやらず、
SELECT で“正しい対応表”を作ってから、アプリやスクリプトで反映する、
というのは現場でもよくあるやり方です。
UPDATE 前に必ずやるべき“安全確認”
「SELECT で対象行を確認する」「件数を数える」
UPDATE を安全に使うための、具体的なチェックを整理します。
一番大事なのは、
「UPDATE と同じ WHERE で、先に SELECT してみる」
ことです。
例:
SELECT *
FROM users
WHERE age >= 30;
SQLで対象を確認してから、
UPDATE users
SET is_active = 0
WHERE age >= 30;
SQLを実行する。
さらに一歩進めるなら、
SELECT COUNT(*) FROM users
WHERE age >= 30;
SQLで「何件更新されるか」を事前に把握しておくのも有効です。
10件のつもりが 10,000 件だった
1件のつもりが 0 件だった
といった“違和感”に気づけるからです。
この「件数を数えてから UPDATE」は、
セキュリティというより “事故防止”のための最低限の防御線 です。
セキュリティの視点から見る「危険な UPDATE」
「WHERE なし」「広すぎる条件」「権限のない人の UPDATE」
UPDATE は、攻撃者にとっても“強い武器”です。
もしアプリケーションに脆弱な SQL 実行部分があり、
そこから UPDATE を自由に打てるとしたら、
全ユーザーのパスワードを無効化する
全ユーザーの is_admin を 1 にする
ログを改ざんして痕跡を消す
といったことが可能になります。
だからこそ、アプリ側では、
アプリケーションから実行できる UPDATE の内容を限定する
プレースホルダを使って、SQL インジェクションを防ぐ
管理系の UPDATE は、権限のあるユーザーだけに絞る
といった対策が必須になります。
Day16 の段階では、
「UPDATE は、攻撃者に渡してはいけない“超強力な権限”でもある」
という感覚だけ持っておいてくれれば十分です。
小さな練習で締める
日本語の「状態変更」を UPDATE に翻訳してみる
頭の中で、次の問いを SQL にしてみてください。
30歳以上のユーザーの status を ‘inactive’ にしたい。
1年以上ログインしていないユーザーの is_active を 0 にしたい。
一度もログインしていないユーザーの status を ‘pending’ にしたい(これは LEFT JOIN と組み合わせるイメージ)。
どれも、
UPDATE
SET 状態フラグ
WHERE 条件(ときに日付・JOIN・サブクエリ)
という型に当てはめれば書けるはずです。
Day16 後半のまとめ
UPDATE は「状態フラグの切り替え」「期間ベースの一括更新」など、実務で頻出する“運用の道具”。
UPDATE の前には、必ず同じ WHERE で SELECT して対象行と件数を確認するのが事故防止の基本。
複雑な更新は、まず SELECT で「どう更新されるべきか」を確認し、必要ならアプリ側で反映する設計も現実的。
UPDATE は、誤操作すればデータ破壊、悪用されればシステム破壊につながる“強い権限”であり、慎重な設計と権限管理が不可欠。
ここまで来たあなたは、
「読むだけの SQL」から一歩進んで、
「データの状態をコントロールする SQL」を扱える段階に入っています。
この先は、INSERT / DELETE、トランザクション、制約設計などと組み合わせて、より安全で堅いデータ操作の世界に進んでいけます。

