コース一覧
SQL実践:JOIN・サブクエリ・データ更新
物理削除と論理削除

SQL実践:JOIN・サブクエリ・データ更新

JOIN、集合演算、サブクエリ、CTE、DDL・DMLを学び、複数テーブルを組み合わせてデータベースを作成・更新する無料コースです。

1
テーブル結合
01. なぜ結合が必要か5分
02. INNER JOINの基本5分
03. LEFT/RIGHT OUTER JOIN5分
04. 複数テーブルの結合5分
05. 自己結合5分
06. 集合演算 UNION/INTERSECT/EXCEPT5分
07. 結合の実践演習5分
08. テーブル結合クイズ5分
2
サブクエリ活用
01. サブクエリとは5分
02. WHERE句のサブクエリ5分
03. FROM句のサブクエリ5分
04. SELECT句のサブクエリ5分
05. WITH句(CTE)の活用5分
06. サブクエリ実践クイズ5分
3
テーブル作成とデータ操作
01. CREATE DATABASE と CREATE TABLE5分
02. データ型の選び方5分
03. INSERT 文でデータを追加する5分
04. UPDATE 文でデータを更新する5分
05. DELETE 文でデータを削除する5分
06. 物理削除と論理削除5分
07. ALTER TABLE で構造を変更する5分
08. 制約 (NOT NULL / UNIQUE / DEFAULT)5分
09. TRUNCATE と DROP5分
10. DDL / DML 実践クイズ5分

物理削除と論理削除

消した翌日に「戻せますか」と言われる

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');

期待される出力

idnameemail
1田中太郎tanaka@example.com
2佐藤花子sato@example.com

ヒント

DELETE ではなく UPDATE で deleted_at に時刻を入れる

有効なユーザは deleted_at IS NULL で絞れる

確認用 SELECT は WHERE deleted_at IS NULL を含む

query.sql
学習モード
エディタを読み込んでいます
コードの実行結果
データベースを初期化中...