行を更新する - UPDATE
既存行の値を書き換える。UPDATE ... FROM / JOIN と、WHERE 忘れによる全件更新事故
概念図
構文
sql
UPDATE table SET col = value WHERE 条件;サンプル
特定ユーザーのステータスを更新する基本形
sql
UPDATE users
SET status = 'active', updated_at = CURRENT_TIMESTAMP
WHERE id = 42;UPDATE の基本
UPDATE は既存行の値を書き換える DML です。SET 列 = 値 をカンマ区切りで複数指定でき、WHERE で対象行を絞り込みます。
WHERE を書かないと全行が更新されます。後述の事故セクションで扱いますが、まずこの原則を覚えてください。
sql
UPDATE products
SET price = price * 1.1,
updated_at = CURRENT_TIMESTAMP
WHERE category = 'book';PostgreSQL の UPDATE ... FROM
PostgreSQL では UPDATE ... FROM で他テーブルを参照しながら更新できます。JOIN 相当の処理を UPDATE で実行する標準的な書き方です。
SQL Server も似た構文(UPDATE ... FROM ... INNER JOIN)をサポートします。
sql
-- PostgreSQL: 注文テーブルから最新金額を反映
UPDATE users u
SET last_order_amount = o.amount
FROM orders o
WHERE o.user_id = u.id
AND o.ordered_at = (
SELECT MAX(ordered_at) FROM orders WHERE user_id = u.id
);MySQL の UPDATE JOIN
MySQL では UPDATE t1 JOIN t2 ON ... SET ... の構文で JOIN を使った更新を書きます。FROM 句は使えないので PostgreSQL とは方言が異なります。
MariaDB も同様の MySQL 構文に従います。SQLite は UPDATE ... FROM を 3.33 以降でサポート。RDBMS 間の差分は RDBMS 方言早見表 にまとめています。
sql
-- MySQL
UPDATE users u
JOIN orders o ON o.user_id = u.id
SET u.last_order_amount = o.amount
WHERE o.status = 'paid';大事故: WHERE 忘れによる全件更新
最も有名な事故が UPDATE users SET status = 'deleted'; のような WHERE 忘れです。全行が一撃で書き換わり、バックアップ以外で復旧する手段はほぼありません。
- 対策 1: 本番作業時は必ず
BEGIN;で明示トランザクションを開始、件数確認してからCOMMIT - 対策 2: MySQL なら
--safe-updatesモードで WHERE なし UPDATE/DELETE を拒否 - 対策 3: 先に
SELECT COUNT(*) FROM ... WHERE ...で件数確認 → 同じ WHERE で UPDATE - 対策 4: RETURNING / OUTPUT で影響行を確認してからコミット
sql
-- 事故防止の型
BEGIN;
SELECT COUNT(*) FROM users WHERE status = 'pending'; -- 件数確認
UPDATE users SET status = 'active' WHERE status = 'pending';
-- 想定件数と合えば COMMIT、違えば ROLLBACK
COMMIT;自己参照 UPDATE と式の評価順
SET a = b, b = a のような自己参照更新を書いたときの評価順は SQL 標準では 更新前の値が使われますが、実装差があります。混乱を避けるため、一時列や CASE 式で明示するのが安全です。
