DELETE 文でデータを削除する
DELETE 文でデータを削除する とは
DELETE文を使って、データベースからデータを削除する操作を学習します。本レッスンでは、DELETE 文でデータを削除する の基本から実際の使いどころまでを整理し、現場で迷わず使える形に落とし込みます。
なぜ重要か
要らなくなった行を消す命令が DELETE です。退会、商品の取り扱い終了、テストデータの掃除など、行を実在しない状態にする操作はアプリのあらゆる場所に登場します。DELETE は UPDATE 以上に「事故ったときの傷が深い」命令です。WHERE 句を書き忘れた DELETE は 全行消えるので、本番では UPDATE と同じ慎重さ、あるいはそれ以上の慎重さで扱う必要があります。一方で、「消したつもり」が長く残り続けると、個人情報・退会済ユーザ・在庫切れ商品などが残骸として溜まり、別の事故を生みます。次のレッスンの「物理削除と論理削除」と組み合わせて、削除の作法を体に染み込ませましょう。
削除はその場で完結する操作に見えて、実は「アプリのコード」「他のテーブルの参照」「監査ログ」「バックアップ」など、周辺要素を巻き込みます。DELETE 1 行を書くときに「これを消したら関連データはどうなるか」を 1 秒考えるクセを付けましょう。
DELETE は UPDATE 以上に怖い命令です。WHERE のない DELETE は全行を消すので、実行前に必ず同じ条件で SELECT して対象行を目視する習慣をつけましょう。
基本構文
SQL クエリ
DELETE FROM users
WHERE id = 1;DELETE FROM テーブル WHERE 条件 の形が基本です。条件を満たした行が全て消えます。WHERE で対象を絞れる点は UPDATE と同じです。
DELETE は「テーブル定義は残し、行だけ消す」命令です。テーブルそのものを消したい場合は DROP TABLE (この章の最後で扱う) を使います。
複数行をまとめて消す場合は IN や比較演算子で条件を広げます。
SQL クエリ
DELETE FROM products
WHERE stock = 0
AND is_discontinued = 1;
DELETE FROM logs
WHERE created_at < '2024-01-01';処理の流れ
外部キー制約 (FOREIGN KEY) が張られている場合、「他のテーブルから参照されている行は消せない」ことがあります (詳しくは次の章)。設計次第で挙動が変わるので、ON DELETE CASCADE を後の章で学びます。
全行削除との違い
SQL クエリ
-- ※ 危険: 全行が消える
DELETE FROM users;全行を一気に消したい場合は TRUNCATE TABLE users; を使う方が高速です (この章の最後で学習)。DELETE は 1 行ずつログを取りながら消すため、全行削除には向きません。1000 万行を DELETE で消すと、ロックが長時間続いて他の処理が詰まることがあります。
LIMIT で安全に試す
PostgreSQL では DELETE に直接 LIMIT は付けられないため、対象行を CTE やサブクエリで絞ってから削除します。本番で「少しずつ消す」時に有効です。
SQL クエリ
WITH target AS (
SELECT id
FROM old_sessions
WHERE created_at < NOW() - INTERVAL '30 days'
ORDER BY id
LIMIT 100
)
DELETE FROM old_sessions
WHERE id IN (SELECT id FROM target);これを cron で繰り返し叩けば、大量データを少しずつ消せます。1 回で全部消そうとすると、ロックが長引いて他のクエリが詰まることがあります。バッチ削除は「少しずつ」の方が本番では優しいです。
トランザクション内で取り消せるか
DELETE はトランザクション内なら ROLLBACK で取り消せます。
SQL クエリ
START TRANSACTION;
DELETE FROM products WHERE stock = 0;
SELECT COUNT(*) FROM products; -- 想定より少ない、と気付いた
ROLLBACK; -- 何事もなかったことにこの「打って確認、ダメなら ROLLBACK」のフローを身につけると、本番作業の安心感が段違いになります。後で学ぶ TRUNCATE / DROP にはこの戻し技は使えません。
注意 DELETE には ORDER BY も書けますが、それでも WHERE 句が無い DELETE は全行消えることに変わりはありません。UPDATE と同じく「先に SELECT で件数を確認してから DELETE する」が基本動作です。
本番運用では「削除= DELETE」とは限りません。「論理削除」 (deleted_at に時刻を入れる) を採用するチームも多く、その場合は DELETE ではなく UPDATE を使います。次のレッスンで詳しく扱います。
DELETE で消した行は完全には消えていないことがあります。MySQL の InnoDB はリサイクル待ちのページとして残しており、
OPTIMIZE TABLEで初めて領域が解放されます。「DELETE したのにディスクが減らない」は普通の現象です。
まとめ
- DELETE は WHERE で対象を絞ってから消す
- WHERE が無いと全行が消える、本番では先に SELECT で確認
- 全行を消したいなら TRUNCATE の方が速い
- LIMIT で少しずつ消す運用も可能
- トランザクション内なら ROLLBACK で戻せる
- 「物理削除か論理削除か」を設計時点で決めておく
- 削除前に
SELECT COUNT(*)で対象件数を確認しておくと、想定外の全行削除を未然に防げる
次のレッスン
次は 物理削除と論理削除 です。物理削除と論理削除の違いを学びます。それぞれのメリット・デメリットを理解しましょう。
事前確認 — 進む前に次の 3 つができることを確認しましょう。
- DELETE の要点を自分の言葉で説明できる
- このレッスンの最小コード (または操作手順) を見ずに書ける
- 練習問題やクイズで間違えた箇所を読み直して理解した
理解度チェック (30 秒)
Q. DELETE とは何か、1 文で説明してください。
A. 本文の「このレッスンで分かること」または冒頭の説明文を見直し、自分の言葉で要約できれば OK。詰まったら本レッスンの最初の H2 セクションを読み返してみましょう。
関連レッスン
テーブル構造
schema.sql
CREATE TABLE products (
id INT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
price INT NOT NULL,
stock INT NOT NULL,
is_discontinued INT NOT NULL DEFAULT 0
);
INSERT INTO products (id, name, price, stock, is_discontinued) VALUES
(1, 'ノートPC', 100000, 10, 0),
(2, 'ワイヤレスマウス', 3000, 0, 1),
(3, '外付けSSD 1TB', 14000, 20, 0),
(4, 'ボールペン', 150, 200, 0),
(5, '古いキーボード', 2000, 0, 1);期待される出力
| id | name | price | stock |
|---|---|---|---|
| 1 | ノートPC | 100000 | 10 |
| 3 | 外付けSSD 1TB | 14000 | 20 |
| 4 | ボールペン | 150 | 200 |