データベースのテーブルから全データを高速に削除したい場面は、開発・テスト・運用のあらゆるフェーズで発生します。そのときに使うのが TRUNCATE TABLE 文です。
DELETE文でも全行削除はできますが、TRUNCATE文は内部的にまったく異なるメカニズムで動作し、圧倒的に高速です。一方で「ロールバックできない」「外部キー制約に引っかかる」などの注意点もあります。
この記事では、TRUNCATE文の基本構文から、DELETE・DROPとの詳細な違い、AUTO_INCREMENTのリセット動作、外部キー制約への対処法、RDBMS別のトランザクション動作差異まで、実務で必要な知識を体系的に解説します。
この記事で学べること
- TRUNCATE TABLE の基本構文と使い方
- DELETE・DROP TABLE との違い(速度・ロールバック・ログ・権限)
- TRUNCATE の内部動作(DDL文としての挙動)
- AUTO_INCREMENT / IDENTITY / SERIAL のリセット動作
- 外部キー制約がある場合の対処法
- トランザクションとの関係(RDBMS別の動作差異)
- CASCADE(PostgreSQL固有の機能)
- RDBMS間比較(MySQL / PostgreSQL / SQL Server / Oracle)
- 権限要件・よくあるエラーと対処法
- 実務パターン(テスト環境リセット・データ入替え等)
TRUNCATE TABLE の基本構文
TRUNCATE文は、指定したテーブルの全行を一括削除するSQL文です。テーブルの構造(カラム定義・インデックス・制約)はそのまま残ります。
基本構文
TRUNCATE TABLE テーブル名;
TABLE キーワードは省略可能ですが、可読性のために付けるのが一般的です。
使用例
-- employeesテーブルの全データを削除
TRUNCATE TABLE employees;
-- TABLEキーワードを省略した場合(同じ動作)
TRUNCATE employees;
注意:TRUNCATE文にはWHERE句を指定できません。常にテーブルの全行が削除されます。条件付きで削除したい場合はDELETE文を使います。
複数テーブルを一度にTRUNCATEする
MySQL・PostgreSQLでは、カンマ区切りで複数テーブルを同時にTRUNCATEできます。
複数テーブルのTRUNCATE(MySQL / PostgreSQL)
TRUNCATE TABLE orders, order_details, payments;
ポイント:SQL ServerとOracleでは1つのTRUNCATE文で複数テーブルを指定できません。個別に実行する必要があります。
DELETE vs TRUNCATE vs DROP TABLE の違い
テーブルのデータを削除する方法は3つあります。それぞれの特性を理解して、適切に使い分けましょう。
| 比較項目 |
DELETE |
TRUNCATE |
DROP TABLE |
| 分類 |
DML |
DDL |
DDL |
| 削除対象 |
行(条件指定可) |
全行 |
テーブルごと |
| WHERE句 |
使用可 |
使用不可 |
なし |
| テーブル構造 |
残る |
残る |
削除される |
| 処理速度 |
遅い(行単位) |
高速 |
高速 |
| ログ |
行単位で記録 |
最小限 |
最小限 |
| ロールバック |
可能 |
RDBMS依存 |
RDBMS依存 |
| トリガー起動 |
起動する |
起動しない |
起動しない |
| AUTO_INCREMENT |
リセットされない |
リセットされる |
テーブルごと消える |
| 外部キー制約 |
子テーブル参照時エラー |
参照されていると不可 |
参照されていると不可 |
3つの削除方法の比較
-- 1. DELETE: 条件付きで行を削除(DML)
DELETE FROM employees WHERE department = '営業部';
-- 2. TRUNCATE: 全行を高速削除(DDL)
TRUNCATE TABLE employees;
-- 3. DROP: テーブル自体を削除(DDL)
DROP TABLE employees;
TRUNCATE の内部動作(DDL文としての挙動)
TRUNCATE文がDELETE文よりも高速な理由は、その内部動作の仕組みにあります。
DELETE文の内部動作
DELETE文はDML(Data Manipulation Language)に分類されます。
- テーブル内の行を1行ずつ削除する
- 削除した各行の情報をトランザクションログに記録する
- 行ごとにDELETE トリガーが起動する
- 削除後もテーブルに割り当てられたデータページは解放されない
TRUNCATE文の内部動作
TRUNCATE文はDDL(Data Definition Language)に分類されます。
- テーブルのデータを格納しているデータページ自体を解放する
- 行ごとのログ記録は行わず、ページ単位の解放のみを記録する
- トリガーは起動しない
- 内部的には「テーブルをDROPして同じ構造で再作成する」に近い動作
DELETE と TRUNCATE のパフォーマンス差
- 100万行のテーブルの場合、DELETEは数分かかることがありますが、TRUNCATEは数秒で完了します
- DELETEは行数に比例して遅くなりますが、TRUNCATEは行数にほぼ依存しません
- DELETEは大量のトランザクションログを生成するため、ログ領域の圧迫にも注意が必要です
注意:TRUNCATEはDDL文のため、多くのRDBMSでは暗黙のCOMMITが発生します。つまり、実行後にROLLBACKしてもデータは戻りません(PostgreSQLは例外)。
AUTO_INCREMENT / IDENTITY のリセット動作
TRUNCATEの重要な特徴の一つが、自動採番カウンターのリセットです。DELETEとの違いを具体的に見てみましょう。
サンプルテーブルの作成
CREATE TABLE products (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50)
);
INSERT INTO products (name) VALUES
('商品A'), ('商品B'), ('商品C');
この時点で id は 1, 2, 3 まで使われています。次の採番は 4 です。
DELETE で全行削除した場合
DELETEではカウンターが継続する
DELETE FROM products;
-- 再度INSERT
INSERT INTO products (name) VALUES ('商品D');
SELECT * FROM products;
実行結果
+----+--------+
| id | name |
+----+--------+
| 4 | 商品D |
+----+--------+
DELETEではidが 4 から再開しています。カウンターはリセットされません。
TRUNCATE で全行削除した場合
TRUNCATEではカウンターがリセットされる
TRUNCATE TABLE products;
-- 再度INSERT
INSERT INTO products (name) VALUES ('商品D');
SELECT * FROM products;
実行結果
+----+--------+
| id | name |
+----+--------+
| 1 | 商品D |
+----+--------+
TRUNCATEではidが 1 からリスタートしています。カウンターがリセットされるのです。
RDBMS別の自動採番リセット動作
| RDBMS |
自動採番 |
TRUNCATEでリセット |
| MySQL |
AUTO_INCREMENT |
リセットされる |
| PostgreSQL |
SERIAL / GENERATED |
RESTART IDENTITY 指定時のみ |
| SQL Server |
IDENTITY |
リセットされる |
| Oracle |
SEQUENCE / GENERATED |
12c以降 GENERATED列はリセット |
PostgreSQL: RESTART IDENTITY の指定
-- シーケンスをリセットしない(デフォルト)
TRUNCATE TABLE products CONTINUE IDENTITY;
-- シーケンスをリセットする
TRUNCATE TABLE products RESTART IDENTITY;
外部キー制約がある場合の対処
外部キー(FOREIGN KEY)で参照されているテーブルをTRUNCATEしようとすると、エラーが発生します。これはデータの整合性を守るための仕組みです。
外部キーを持つテーブル構成
-- 親テーブル
CREATE TABLE departments (
id INT PRIMARY KEY,
name VARCHAR(50)
);
-- 子テーブル(departmentsを参照)
CREATE TABLE employees (
id INT PRIMARY KEY,
name VARCHAR(50),
dept_id INT REFERENCES departments(id)
);
TRUNCATEするとエラーになる例
-- 子テーブルから参照されているためエラー
TRUNCATE TABLE departments;
エラー例(MySQL)
ERROR 1701 (42000): Cannot truncate a table referenced
in a foreign key constraint
対処法1:子テーブルを先にTRUNCATEする
最もシンプルな方法です。参照関係の末端(子テーブル)から順番に削除します。
子テーブルから順にTRUNCATE
-- 1. 子テーブルを先に削除
TRUNCATE TABLE employees;
-- 2. 親テーブルを削除
TRUNCATE TABLE departments;
対処法2:外部キーチェックを一時的に無効化する(MySQL)
MySQL: 外部キーチェックを無効化
-- 外部キーチェックを無効化
SET FOREIGN_KEY_CHECKS = 0;
-- 自由にTRUNCATEできる
TRUNCATE TABLE departments;
TRUNCATE TABLE employees;
-- 外部キーチェックを再度有効化
SET FOREIGN_KEY_CHECKS = 1;
注意:FOREIGN_KEY_CHECKS を無効にした状態でデータを投入すると、整合性のないデータが入る可能性があります。必ず作業後に SET FOREIGN_KEY_CHECKS = 1; で再有効化してください。
対処法3:CASCADE を使う(PostgreSQL)
PostgreSQLでは CASCADE オプションを指定すると、参照している子テーブルのデータも自動的に削除されます。
PostgreSQL: CASCADE で連鎖削除
-- departmentsとそれを参照するテーブルのデータも削除
TRUNCATE TABLE departments CASCADE;
-- RESTART IDENTITYと組み合わせることも可能
TRUNCATE TABLE departments RESTART IDENTITY CASCADE;
ポイント:CASCADE はPostgreSQL固有の機能です。MySQL・SQL Server・Oracleでは使えません。また、CASCADE の反対として RESTRICT(参照がある場合はエラー、デフォルト動作)もあります。
TRUNCATE とトランザクション(RDBMS別の動作差異)
TRUNCATEがトランザクション内でどう動作するかは、RDBMSによって大きく異なります。これは実務で最も注意すべきポイントの一つです。
| RDBMS |
トランザクション内の動作 |
ROLLBACK可否 |
| MySQL (InnoDB) |
暗黙のCOMMITが発生 |
不可 |
| PostgreSQL |
トランザクション内で動作 |
可能 |
| SQL Server |
トランザクション内で動作 |
可能 |
| Oracle |
暗黙のCOMMITが発生 |
不可 |
MySQL: 暗黙のCOMMITが発生する
MySQL: ROLLBACKしてもデータは戻らない
START TRANSACTION;
TRUNCATE TABLE products;
-- ここで暗黙のCOMMITが発生 → 前のトランザクションは確定
ROLLBACK;
-- TRUNCATEは既にCOMMIT済みのため、データは戻らない
PostgreSQL: ROLLBACKでデータを復元できる
PostgreSQL: TRUNCATEをROLLBACKできる
BEGIN;
TRUNCATE TABLE products;
-- まだCOMMITされていない
ROLLBACK;
-- データが復元される!
SQL Server: ROLLBACKでデータを復元できる
SQL Server: トランザクション内で安全にTRUNCATE
BEGIN TRANSACTION;
TRUNCATE TABLE products;
-- 確認してからCOMMITまたはROLLBACK
SELECT COUNT(*) FROM products;
-- 0件なのを確認
COMMIT;
-- または ROLLBACK; でデータを復元
暗黙のCOMMITが発生するDDL文(MySQL / Oracle)
TRUNCATE TABLE
CREATE TABLE / ALTER TABLE / DROP TABLE
CREATE INDEX / DROP INDEX
RENAME TABLE
- これらの文をトランザクション中に実行すると、その前までの変更も自動的にCOMMITされます
RDBMS間の構文比較
TRUNCATEの構文と利用可能なオプションはRDBMSによって異なります。以下に主要RDBMSの対応状況をまとめます。
| 機能 |
MySQL |
PostgreSQL |
SQL Server |
Oracle |
| TRUNCATE TABLE |
○ |
○ |
○ |
○ |
| TABLE省略 |
○ |
○ |
○ |
○ |
| 複数テーブル同時 |
○ |
○ |
× |
× |
| CASCADE |
× |
○ |
× |
× |
| RESTART IDENTITY |
×(自動) |
○ |
×(自動) |
× |
| ROLLBACK可能 |
× |
○ |
○ |
× |
| トリガー起動 |
× |
ON TRUNCATE |
× |
× |
| REUSE STORAGE |
× |
× |
× |
○ |
Oracle固有のオプション
Oracleでは REUSE STORAGE / DROP STORAGE オプションが使えます。
Oracle: ストレージオプション
-- ストレージ領域を解放する(デフォルト)
TRUNCATE TABLE products DROP STORAGE;
-- ストレージ領域を保持する(再投入予定の場合に高速)
TRUNCATE TABLE products REUSE STORAGE;
ポイント:REUSE STORAGE はデータを削除した後に大量のデータを再投入する予定がある場合に有効です。ストレージ領域の再確保が不要になるため、INSERT性能が向上します。
権限要件
TRUNCATEを実行するために必要な権限は、RDBMSごとに異なります。DELETEよりも高い権限が必要な場合が多いことに注意してください。
| RDBMS |
必要な権限 |
| MySQL |
DROP権限 |
| PostgreSQL |
テーブル所有者、またはTRUNCATE権限 |
| SQL Server |
ALTER権限(db_ddladminロール等) |
| Oracle |
テーブル所有者、またはDROP ANY TABLE権限 |
PostgreSQL: TRUNCATE権限の付与
-- 特定ユーザーにTRUNCATE権限を付与
GRANT TRUNCATE ON products TO app_user;
-- スキーマ内の全テーブルに対して付与
GRANT TRUNCATE ON ALL TABLES IN SCHEMA public TO app_user;
注意:MySQLでTRUNCATEを実行するにはDELETE権限ではなくDROP権限が必要です。DELETE権限しか持っていないユーザーでTRUNCATEを実行するとエラーになります。
よくあるエラーと対処法
エラー1:外部キー制約による拒否
エラーメッセージ(MySQL)
ERROR 1701 (42000): Cannot truncate a table referenced
in a foreign key constraint (`db`.`child_table`,
CONSTRAINT `fk_parent` FOREIGN KEY (`parent_id`)
REFERENCES `db`.`parent_table` (`id`))
対処法
-- 方法A: 子テーブルを先にTRUNCATE
TRUNCATE TABLE child_table;
TRUNCATE TABLE parent_table;
-- 方法B: 外部キーチェックを一時無効化(MySQL)
SET FOREIGN_KEY_CHECKS = 0;
TRUNCATE TABLE parent_table;
SET FOREIGN_KEY_CHECKS = 1;
-- 方法C: CASCADE(PostgreSQLのみ)
TRUNCATE TABLE parent_table CASCADE;
エラー2:権限不足
エラーメッセージ(MySQL)
ERROR 1142 (42000): DROP command denied to user
'app_user'@'localhost' for table 'products'
対処法: DROP権限を付与
-- MySQL: DROP権限を付与
GRANT DROP ON database_name.products TO 'app_user'@'localhost';
FLUSH PRIVILEGES;
エラー3:ビューに対するTRUNCATE
エラーメッセージ(MySQL)
ERROR 1146 (42S02): Table 'db.view_name' doesn't exist
TRUNCATEはテーブルにのみ使用できます。ビューに対しては使用できません。ビューのデータを削除したい場合は、元テーブルに対してDELETEまたはTRUNCATEを実行します。
エラー4:テンポラリテーブルとの注意点
テンポラリテーブルに対するTRUNCATEは、ほとんどのRDBMSでサポートされていますが、SQL Serverではトランザクション内でのテンポラリテーブルに対するTRUNCATE後にROLLBACKするとエラーになる場合があります。
実務パターン
パターン1:テスト環境のデータリセット
開発・テスト環境で、テストデータを初期状態に戻す場面で最も頻繁に使われます。
テスト環境リセットスクリプト(MySQL)
-- テスト環境のデータリセット
SET FOREIGN_KEY_CHECKS = 0;
TRUNCATE TABLE order_details;
TRUNCATE TABLE orders;
TRUNCATE TABLE payments;
TRUNCATE TABLE customers;
TRUNCATE TABLE products;
SET FOREIGN_KEY_CHECKS = 1;
-- テストデータの再投入
SOURCE /path/to/test_data.sql;
パターン2:ETL処理でのデータ入替え
データウェアハウスやレポーティングテーブルで、古いデータを削除してから新しいデータを投入する場面です。
ETL処理での活用(PostgreSQL)
BEGIN;
-- 既存データをクリア
TRUNCATE TABLE daily_report RESTART IDENTITY;
-- 新しいデータを投入
INSERT INTO daily_report (report_date, category, total_sales)
SELECT
sale_date,
category,
SUM(amount)
FROM sales
WHERE sale_date = CURRENT_DATE
GROUP BY sale_date, category;
COMMIT;
-- PostgreSQLならエラー時にROLLBACKでき、データ消失を防げる
パターン3:ログテーブルの定期クリア
ログテーブルのクリア
-- 全ログを削除する場合はTRUNCATEが高速
TRUNCATE TABLE access_logs;
-- 古いログだけ残す場合はDELETEを使う
DELETE FROM access_logs
WHERE created_at < DATE_SUB(NOW(), INTERVAL 90 DAY);
ポイント:全データ削除ならTRUNCATE、条件付き削除ならDELETEと使い分けるのが鉄則です。「全行DELETE」は非効率なので、TRUNCATEに置き換えることを検討しましょう。
パターン4:マイグレーション時のテーブルリセット
安全なリセット手順
-- 1. バックアップを取得
CREATE TABLE products_backup AS
SELECT * FROM products;
-- 2. TRUNCATEでデータをクリア
TRUNCATE TABLE products;
-- 3. 新しいデータを投入
INSERT INTO products (id, name, price)
SELECT id, name, new_price
FROM migration_source;
-- 4. 問題なければバックアップを削除
DROP TABLE products_backup;
TRUNCATEを使うべき場面・避けるべき場面
| 場面 |
推奨 |
理由 |
| テスト環境のリセット |
TRUNCATE |
全データ削除+ID初期化が目的 |
| ログテーブルの全クリア |
TRUNCATE |
高速・ログ領域を節約 |
| ETLのデータ入替え(PostgreSQL) |
TRUNCATE |
トランザクションで安全に入替え可 |
| 条件付きの行削除 |
DELETE |
TRUNCATEはWHERE句不可 |
| 削除トリガーの実行が必要 |
DELETE |
TRUNCATEはトリガーが起動しない |
| ロールバックの可能性がある(MySQL/Oracle) |
DELETE |
暗黙COMMITで戻せない |
| テーブル自体が不要 |
DROP TABLE |
構造も削除する場合 |
まとめ
| ポイント |
内容 |
| 基本構文 |
TRUNCATE TABLE テーブル名; |
| DELETE との違い |
DDL文・高速・ログ最小・トリガー不起動 |
| AUTO_INCREMENT |
TRUNCATEでリセットされる(DELETEではされない) |
| 外部キー制約 |
参照されていると不可。子テーブル先行 or FK無効化 or CASCADE |
| ROLLBACK |
PostgreSQL/SQL Serverは可能、MySQL/Oracleは不可 |
| CASCADE |
PostgreSQL固有。参照先も連鎖的にTRUNCATE |
| 権限 |
DELETEより高い権限が必要(MySQL: DROP権限) |
TRUNCATEは全データ削除を高速に行うための強力なコマンドですが、DELETE文とは根本的に異なるDDL文です。「ロールバック可否」「トリガーの有無」「AUTO_INCREMENTのリセット」など、DELETEとの違いを正しく理解したうえで使い分けましょう。
特にMySQL・Oracleでは暗黙のCOMMITが発生するため、本番環境での実行前には必ずバックアップを取得してから作業することをお勧めします。
関連記事:DELETE文でデータを削除する方法 / INSERT文でデータを追加する方法 / CREATE TABLEでテーブルを作成する方法