MySQLのDELETE・TRUNCATE・DROPの違いと使い分け

black laptop computer turned-on displaying source code on table SQL

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つあります。

  1. 子テーブルのデータを先に DELETE または TRUNCATE してから親テーブルを TRUNCATE する
  2. 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つの特性の違いを把握して、用途に合ったコマンドを適切に選びましょう。

参考リンク

アイキャッチ画像: Photo by Jantine Doornbos on Unsplash

タイトルとURLをコピーしました