MySQLでデータを削除するコマンドは DELETE、TRUNCATE、DROP の3種類があります。どれも「削除」に関連しますが、動作は根本から異なります。DELETE は WHERE 句で対象行を絞り込んでロールバックもできるDMLです。TRUNCATE は全行を高速消去するDDLで、ロールバックは不可です。DROP はテーブル定義ごと削除します。本記事では3つの挙動の違いを実例と比較表で整理し、どの場面でどれを選ぶべきかの判断基準を示します。
このシナリオで考える
以下の orders(注文)テーブルを例に使います。id、user_id、amount、status の4カラムを持つシンプルな構成です。
CREATE TABLE orders (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
amount DECIMAL(10,2) NOT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'pending'
);
INSERT INTO orders (user_id, amount, status) VALUES
(1, 1500.00, 'completed'),
(2, 980.00, 'pending'),
(3, 2300.00, 'cancelled'),
(4, 500.00, 'completed');
id=1〜4 の4行が入った状態を出発点として、DELETE・TRUNCATE・DROP それぞれを試してみましょう。
DELETEの基本:条件を指定して行を削除する
DELETE はDML(データ操作言語)に分類されます。WHERE 句で削除対象の行を細かく絞り込めるのが最大の特徴で、省略するとテーブルの全行が削除されます。
-- status が 'cancelled' の行だけ削除
DELETE FROM orders WHERE status = 'cancelled';
-- WHERE を省略すると全行削除(要注意)
DELETE FROM orders;
DELETE の最も重要な特性は、トランザクション内でロールバックが可能なことです。誤って削除した場合でも ROLLBACK で元に戻せます。
START TRANSACTION;
DELETE FROM orders WHERE user_id = 1;
SELECT * FROM orders; -- user_id=1 の行が見えない
ROLLBACK;
SELECT * FROM orders; -- user_id=1 の行が復元される
一方で、行を1件ずつ処理しながらundo logを記録し、インデックスの更新も行います。そのため大量行の削除では TRUNCATE より低速です。ON DELETE トリガーが設定されている場合は、削除のたびにトリガーが実行される点も覚えておきましょう。AUTO_INCREMENT カウンタはリセットされず、全行削除後に新しい行を挿入すると id は削除前の最大値の続番から始まります。
TRUNCATEの特性:全行を高速消去してAUTO_INCREMENTをリセット
TRUNCATE TABLE はDDL(データ定義言語)として扱われます。内部的にはテーブルを一度削除して再作成するような処理なので、行を1件ずつ処理する DELETE よりはるかに高速です。WHERE 句は使えず、テーブルの全行が削除対象になります。
TRUNCATE TABLE orders;
実行後、テーブルは空になり、AUTO_INCREMENT カウンタは1にリセットされます。テスト環境でデータをリセットして id を1から振り直したい場合に便利です。
-- TRUNCATE後に挿入すると id が1から始まる
INSERT INTO orders (user_id, amount, status) VALUES (5, 3000.00, 'pending');
SELECT id FROM orders; -- id = 1
注意すべき点が2つあります。DDL は暗黙的な COMMIT を発生させるため、TRUNCATE はロールバックできません。トランザクション内で実行しても ROLLBACK では元に戻せないので、実行前には必ずバックアップを確認しましょう。もう一点、ON DELETE トリガーは実行されず、外部キー制約(FOREIGN KEY)で参照されている親テーブルを TRUNCATE しようとするとエラーになります。
DROPの役割:テーブル定義ごと削除する
DROP TABLE はテーブルの定義(カラム構成・インデックス・制約)をデータごと完全に削除します。テーブル自体がなくなるため、再利用するには CREATE TABLE からやり直す必要があります。開発中の不要テーブルの削除や旧テーブルのクリーンアップ、マイグレーション処理での使い捨てテーブル削除などが代表的な用途です。
DROP TABLE orders;
-- テーブルが存在しない場合もエラーにしない(冪等な操作に便利)
DROP TABLE IF EXISTS orders;
外部キーで他テーブルから参照されているテーブルは DROP できません。参照している子テーブルを先に DROP するか、ALTER TABLE で外部キー制約を削除してから実行する順序が必要です。
3つのコマンドの比較
3つのコマンドの違いを一覧にまとめます。
| 比較項目 | DELETE | TRUNCATE | DROP |
|---|---|---|---|
| 削除対象 | 指定した行 / 全行 | 全行 | テーブルごと |
| WHERE句 | 使える | 使えない | 使えない |
| ロールバック | 可能 | 不可(暗黙COMMIT) | 不可 |
| AUTO_INCREMENT | リセットされない | 1にリセット | テーブルごと消える |
| ON DELETEトリガー | 実行される | 実行されない | 実行されない |
| 外部キー制約あり | 制約に従う | エラー(参照あり時) | エラー(参照元あり時) |
| 速度(全行削除時) | 遅い | 速い | 速い |
| DDL / DML 区分 | DML | DDL | DDL |
よくある落とし穴と注意点
TRUNCATE のロールバック不可は実務での事故原因として特に多いパターンです。本番環境でのデータ整理作業では、実行前に必ず mysqldump 等でバックアップを取りましょう。動作確認が必要なときは DELETE から始め、問題がなければ TRUNCATE に切り替えるのが安全です。
DELETE の WHERE 句書き忘れも注意が必要です。DELETE FROM orders; と書けば全行が消えます。実行前に SELECT COUNT(*) FROM orders WHERE ... で削除対象の件数を確認する習慣をつけることを推奨します。
外部キーと TRUNCATE の相性も理解しておきましょう。子テーブルから参照されている親テーブルを TRUNCATE するとエラーになります。回避策は2つあります。
- 子テーブルのデータを先に DELETE または TRUNCATE してから親テーブルを TRUNCATE する
SET FOREIGN_KEY_CHECKS = 0;で制約チェックを一時無効化してから TRUNCATE し、完了後にSET FOREIGN_KEY_CHECKS = 1;に戻す(本番環境では慎重に使う)
AUTO_INCREMENT の扱いも場面によって重要です。DELETE で全行を消してもカウンタは残るため、次に挿入される id は削除前の最大値 +1 になります。番号を1から振り直したい場合は TRUNCATE が適しています。反対に、ON DELETE トリガーで連携処理が必要な場合は DELETE を使いましょう。TRUNCATE ではトリガーが実行されません。
まとめ
DELETE は「条件を指定して安全に削除したい」「ロールバックの余地を残したい」ときに選びます。TRUNCATE は「テーブルを高速に全消去して AUTO_INCREMENT もリセットしたい」ときに選びます。DROP は「テーブル自体が不要になった」ときに使います。3つの特性の違いを把握して、用途に合ったコマンドを適切に選びましょう。
参考リンク
- MySQL 8.0 Reference Manual: DELETE Statement
- MySQL 8.0 Reference Manual: TRUNCATE TABLE Statement
- MySQL 8.0 Reference Manual: DROP TABLE Statement
アイキャッチ画像: Photo by Jantine Doornbos on Unsplash
