物理削除と論理削除
消した翌日に「戻せますか」と言われる
DELETE は行そのものをテーブルから取り除きます。実行した瞬間は気持ちよく片付きますが、翌日に「あの行、間違って消しました。戻せますか」と言われても、バックアップから復旧する以外に手はありません。
しかも運用が始まると、削除には削除以外の要求がくっついてきます。退会したユーザの過去の売上は集計に残したい、監査のために誰がいつ消したかを追いたい、間違いに気づいたら取り消したい。行を本当に消してしまうと、これらは全部できなくなります。
行を実際に取り除くやり方を物理削除、行は残したまま「もう無いことにする」やり方を論理削除と呼びます。
行を残したまま、無いことにする
論理削除は、削除した時刻を入れる列を 1 本足すところから始めます。
SQL クエリ
ALTER TABLE articles ADD COLUMN deleted_at TIMESTAMP NULL;削除の代わりに、その列へ時刻を書き込みます。行は残り、deleted_at が入っているかどうかが「生きているか」の目印になります。
SQL クエリ
-- 記事を削除済みにする
UPDATE articles
SET deleted_at = NOW()
WHERE slug = 'summer-campaign';代わりに、読む側が責任を負うことになります。生きている行だけが欲しいクエリには、毎回この条件を足します。
SQL クエリ
SELECT id, title
FROM articles
WHERE deleted_at IS NULL;条件を 1 か所でも書き忘れると、消したはずの行が一覧に出てきます。ログイン判定のような場所で忘れれば、退会済みのアカウントでログインできる事故になります。実務ではこの条件をアプリの Repository 層で自動的に足す形にして、書き忘れを潰します。
どちらを選ぶか
| 消したいもの | 選ぶ側 | 理由 |
|---|---|---|
| 注文や取引の履歴 | 論理削除 | 消えると会計や集計が合わなくなる |
| 誤操作からの復元が要る画面 | 論理削除 | 元に戻す操作が UPDATE 1 回で済む |
| 明らかなゴミ、試験投入したデータ | 物理削除 | 残す価値が無く、条件を汚すだけ |
| 削除請求された個人情報 | 物理削除 | フラグを立てただけでは応えたことにならない |
最後の行が要注意です。論理削除は「削除しました」と画面に出すための仕組みであって、個人情報を持ち続けてよい理由にはなりません。氏名やメールアドレスは別途マスクするか、行ごと物理削除します。
論理削除を入れると
UNIQUE制約と衝突することがあります。メールアドレスがUNIQUEのテーブルでは、退会した行が残っているせいで同じ人が再登録できません。(email, deleted_at)の複合UNIQUEにするなど、逃げ道を先に決めておきます。
テーブル構造
CREATE TABLE users (
id INT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(255) NOT NULL,
deleted_at TIMESTAMP NULL
);
INSERT INTO users (id, name, email, deleted_at) VALUES
(1, '田中太郎', 'tanaka@example.com', NULL),
(2, '佐藤花子', 'sato@example.com', NULL),
(3, '退会済 A', 'old-a@example.com', '2024-12-01 10:00:00'),
(4, '山田次郎', 'yamada@example.com', NULL),
(5, '退会済 B', 'old-b@example.com', '2025-01-15 09:30:00');期待される出力
| id | name | |
|---|---|---|
| 1 | 田中太郎 | tanaka@example.com |
| 2 | 佐藤花子 | sato@example.com |