6.

TRUNCATE TABLE とは|DELETE / DROP との違いとROLLBACKできない理由

編集
この記事の要点
  • TRUNCATE TABLE = 表の中身を全部消して、表そのものは残す
  • DELETE との違い: TRUNCATE は行を 1 行ずつ消さず、領域ごと解放するので圧倒的に速い。ただし WHERE で絞れないトリガも動かない
  • DROP との違い: DROP は表の定義ごと消える。TRUNCATE は空の表が残るので、そのまま INSERT できる
  • DDL 扱いのため暗黙コミットされる DB が多い(MySQL / Oracle)。ROLLBACK で戻せないのが最大の注意点
  • 他の表から参照されている(外部キーの参照先になっている)表は、そのままでは TRUNCATE できない

TRUNCATE TABLE とは

TRUNCATE TABLE は、表に入っているデータをすべて削除し、表の構造(カラム定義・インデックス・制約)はそのまま残す SQL です。「中身を空にして作り直したい」ときに使います。

-- 基本形
TRUNCATE TABLE users;

-- Oracle / PostgreSQL では TABLE を省略できない DB もあるが
-- MySQL は TABLE を省略しても動く
TRUNCATE users;

DELETE / TRUNCATE / DROP の違い

この 3 つは「消す」という点では同じですが、何が残るか取り消せるかが違います。ここを取り違えると本番でデータを失います。

観点DELETETRUNCATEDROP
分類DMLDDLDDL
表の定義残る残る消える
データ条件に合う行全行全行
WHERE で絞るできるできないできない
ROLLBACKできる原則できない原則できない
速度行数に比例して遅い速い速い
トリガ動く動かない動かない
AUTO_INCREMENT継続1 に戻る(表ごと消える)

使い分けの目安は次のとおりです。一部だけ消すなら DELETE全部消して同じ表を使い続けるなら TRUNCATE表そのものが不要なら DROP。テストデータの入れ直しや、日次で洗い替えする中間テーブルの初期化は TRUNCATE の典型的な出番です。

ROLLBACK で戻せない

いちばん事故が多いのがここです。TRUNCATE は多くの DB で DDL として扱われ、実行時に暗黙のコミットが走ります。トランザクションの中で実行しても、ROLLBACK で元に戻せません。

-- MySQL / Oracle: これは戻せない
START TRANSACTION;
TRUNCATE TABLE users;   -- ここで暗黙コミットが走る
ROLLBACK;               -- 効かない。users は空のまま

-- DELETE なら戻せる
START TRANSACTION;
DELETE FROM users;
ROLLBACK;               -- 元に戻る
DBMSトランザクション内での TRUNCATE
MySQL暗黙コミット。ROLLBACK で戻せない
Oracle暗黙コミット。ROLLBACK で戻せない
SQL Serverトランザクション内なら ROLLBACK できる
PostgreSQLトランザクション内なら ROLLBACK できる

MySQL と Oracle では取り消せないので、実行前にバックアップを取るのが前提になります。SQL Server と PostgreSQL は戻せますが、他の DB へ移植したときに前提が崩れるため、頼りきらないほうが安全です。

外部キーがあると失敗する

その表が他の表から外部キーで参照されている場合、TRUNCATE は拒否されます。参照している側に孤立した行が残ってしまうためです。

ERROR 1701 (42000): Cannot truncate a table referenced in a foreign key constraint
(MySQL の例)

対処は 3 つあります。

方法内容注意
子から先に消す参照している側の表を先に TRUNCATE するいちばん安全
DELETE を使うDELETE FROM users; に置き換える遅いが確実。ON DELETE CASCADE も効く
制約を一時無効化MySQL なら SET FOREIGN_KEY_CHECKS = 0;整合性が壊れる恐れ。作業後に必ず 1 に戻す

PostgreSQL には TRUNCATE parent, child CASCADE; のように、参照している表もまとめて空にする構文があります。強力なぶん、意図しない表まで空になっていないか実行前に確認してください。

AUTO_INCREMENT / シーケンスの挙動

連番カラムの扱いも DELETE と違います。TRUNCATE は採番を初期値に戻します

-- MySQL
INSERT INTO t (name) VALUES ('a'),('b'),('c');   -- id = 1,2,3

DELETE FROM t;
INSERT INTO t (name) VALUES ('d');               -- id = 4 (続きから)

TRUNCATE TABLE t;
INSERT INTO t (name) VALUES ('e');               -- id = 1 (1 に戻る)

PostgreSQL では既定で採番が戻らないため、戻したい場合は TRUNCATE t RESTART IDENTITY; と書きます。テストの再現性を保ちたいときはこの違いが効いてきます。

権限とレプリケーション

TRUNCATE は DDL なので、必要な権限も DELETE とは別です。MySQL では DROP 権限、Oracle では DROP ANY TABLE(自分のスキーマ外の場合)が要ります。「DELETE はできるのに TRUNCATE で権限エラーになる」場合はここを疑ってください。

レプリケーション環境では、TRUNCATE は 1 文としてレプリカに伝わるため、数百万行の削除でもバイナリログはほとんど増えません。同じことを DELETE でやると全行が行イメージとして記録され、ログが膨らんでレプリカ遅延の原因になります。大量データの全削除は TRUNCATE のほうがレプリケーションにやさしい、という判断材料になります。

FAQ

Q: TRUNCATE したデータは復旧できますか?
A: MySQL / Oracle では ROLLBACK では戻せません。バックアップかバイナリログ / REDO ログからの復旧になります。作業前のバックアップが唯一の保険です。

Q: 一部の行だけ消したいのですが
A: TRUNCATE に WHERE はありません。DELETE FROM t WHERE ... を使ってください。

Q: TRUNCATE のあと領域は解放されますか?
A: MySQL の InnoDB で innodb_file_per_table が有効なら、表ファイルが作り直されてディスクが解放されます。共有テーブルスペースの場合は、ファイルサイズは縮みません。

Q: 全部の表をまとめて空にしたい
A: 外部キーの依存順に注意しながら 1 表ずつ実行します。順序が面倒な場合は、参照している側から先に消すか、DELETE と ON DELETE CASCADE に切り替えるのが確実です。

編集
Post Share
子ページ

子ページはありません

同階層のページ
  1. 表の作成
  2. カラムの追加
  3. カラムの定義変更
  4. カラムの削除
  5. 表の削除
  6. TRUNCATE文