TRUNCATE — 全件削除の高速版とハイウォーターマーク

DELETE と TRUNCATE の違いを図で解説します。ハイウォーターマーク (HWM) のしくみ、実行後に取り消せるかどうかの違い、速度差の正体、Oracle / PostgreSQL / MySQL での使い分けまで押さえます。

本記事のコードは本番 RDBMS 用の例示

本記事で扱う TRUNCATE TABLE 構文は MySQL / PostgreSQL / Oracle / SQL Server などの本番 RDBMS で使う書き方で、本講座のブラウザ用コンソール(SQLite 採用)では TRUNCATE キーワードを直接は受け付けません。SQLite では DELETE FROM 表; が同じ効果を持ち、内部の truncate 最適化 によって TRUNCATE と同等の速度で動きます。

TRUNCATE — 全件を高速に削除する

テーブルの 全行をまとめて削除する 操作には、2 種類の書き方があります。1 つは 前の記事で扱った DELETE FROM 表;(WHERE なし)、もう 1 つが本記事で扱う `TRUNCATE TABLE 表;` です。どちらも結果としてテーブルが空になりますが、動き方と速度がまったく違います

DELETE行を 1 行ずつ削除する DML(Data Manipulation Language = データ操作言語)命令で、削除した行ごとにログを残しながら処理します。一方 TRUNCATEテーブル全体をリセットする DDL(Data Definition Language = データ定義言語)寄りの命令で、行ごとの記録をせず、テーブル定義そのものを「ほぼ作り直し」のように初期化します。

DELETE と TRUNCATE の動きの違い
DELETE FROM table;TRUNCATE TABLE table;行を 1 つずつ削除(WHERE で絞り込み可)テーブルを一度にリセット(WHERE は使えない)DML 扱い実行後の取り消しが可能DDL 扱い (DB による)実行で確定して戻せないことも行ごとに削除ログトリガー起動ログは最小限トリガーは原則起動しない大量行で遅くなる大量行でも一瞬
DELETE は行ごとに削除して記録を残す DML、TRUNCATE はテーブル全体を一度にリセットする DDL 寄りの操作です。WHERE の有無、実行後の取り消し可否、速度がそれぞれ違います。
-- 1) DELETE で全件削除(行ごとに削除ログを残す、実行後の取り消しが可能)
DELETE FROM customer;

-- 2) TRUNCATE で全件削除(一括リセット、行ログは残さない、原則実行と同時に確定)
TRUNCATE TABLE customer;

-- ※ 本講座のコンソール(SQLite)では TRUNCATE キーワードは使わず、
--   DELETE FROM customer; が内部で truncate 最適化されて高速に動きます。

MySQL / PostgreSQL / Oracle での違い

TRUNCATE TABLE 表; の構文自体は MySQL / PostgreSQL / Oracle / SQL Server で共通ですが、細部の挙動は DB ごとに違います。

- MySQL: DDL 扱いで実行後の取り消し不可、AUTO_INCREMENT がリセットされる

- PostgreSQL: 実行後の取り消しが可能、RESTART IDENTITY で連番もリセットできる

- Oracle: DDL 扱いで実行後の取り消し不可、REUSE STORAGE / DROP STORAGE で領域の戻し方を選べる

- SQL Server: 実行後の取り消しが可能

ハイウォーターマーク — 速度差の正体

DELETE と TRUNCATE の速度差を理解する鍵が ハイウォーターマーク (High Water Mark, HWM) という概念です。HWM は テーブルがこれまでに使ったことのあるブロック (storage block) の最高位置 を指す目印で、Oracle が伝統的に使ってきた用語ですが、PostgreSQL / SQL Server / MySQL (InnoDB) でも 同等の最適化 が内部で働いています。

DB がフルスキャン(SELECT * FROM 表 のような全行走査)を行うときは、テーブルの先頭から HWM までの全ブロック を順番に読みます。HWM の位置が高いほど読むブロックが多くなり、空のブロックがあっても読み飛ばさないため、空のテーブルでも HWM が高ければスキャンに時間がかかります

ハイウォーターマーク (HWM) のしくみ
スキャン対象 = HWM 以下のブロック── HWM (block 5) ──テーブルが過去に使った最高位置block 5: ●データフルスキャンはここから下を全部読むblock 4: ●データblock 3: ●データblock 2: ●データblock 1: ●データテーブルの先頭(block 0)
テーブルの内部はブロック (固定サイズの記憶領域) の連なりで、HWM は「これまでに使った一番上のブロック」を指す目印です。フルスキャンは HWM 以下のブロックをすべて読みます。

ここで重要なのが DELETE では HWM が下がらない という挙動です。DELETE FROM 表; で全行を削除しても、テーブルの中身は空になるだけで、ブロックそのものはセグメントに残ったまま。HWM の位置も block 5 のままです。次に SELECT COUNT(*) FROM 表; を流すと、行数は 0 ですが HWM 以下のブロックを最後まで全部読みに行く ため、削除前と同じくらいの時間がかかります。

TRUNCATE TABLE 表;HWM をテーブル先頭に戻す 命令で、フルスキャンの対象範囲が一気に 0 ブロックになります。これが「TRUNCATE は大量行でも一瞬で終わる」と言われる正体です。

DELETE 後 と TRUNCATE 後 の HWM 比較
状態初期 (5 行)DELETE FROMtable;TRUNCATE TABLEtable;行数5 行0 行0 行ブロック5 個埋まる5 個 (空)リセットHWMblock 5block 5のままblock 0リセットフルスキャン5 ブロック5 ブロック(空も含む)0 ブロック(一瞬)
削除後の 行数 はどちらも 0 ですが、HWM の位置 が違うため、その後のフルスキャン速度に大きな差が出ます。

DELETE をしてもデータ量が減らない?

実務でデータ量を減らすために DELETE を流したのに、ストレージ使用量がほとんど減らない という事象に遭遇したことがあります。

原因はまさに HWM が下がらない ことでした。DELETE は行を論理的に消すだけで、テーブルが確保していたブロック(ページ)はそのまま残ります。ファイル上は領域を持ったままなので、OS から見たデータ量は変わりません。

領域そのものを縮めたいときは、DB ごとに用意された再構成コマンド(PostgreSQL の VACUUM FULL、MySQL (InnoDB) の OPTIMIZE TABLE、Oracle の ALTER TABLE ... SHRINK SPACE など)を実行するか、全件削除なら TRUNCATE を選びます。

DELETE と TRUNCATE の使い分け

実務では「全件消すなら TRUNCATE、条件付きなら DELETE」という単純な分け方ではなく、実行後の取り消しが必要かトリガーの有無処理速度の重要度 の 3 つで判断します。次の比較表が判断の基準になります。

項目DELETETRUNCATE
削除単位行ごとテーブル全体
WHERE 条件使える使えない (常に全件)
速度 (大量行)遅い (行ごとに処理)速い (一瞬)
実行後の取り消し可能DB による (Oracle / MySQL は不可)
トリガー起動起動する原則起動しない
AUTO_INCREMENT維持されるリセットされる (DB による)
ハイウォーターマーク動かないリセットされる
削除ログ行ごとに記録 (大量)最小限
分類DMLDDL (DB による)

TRUNCATE の落とし穴

TRUNCATE は速くて便利ですが、取り返しがつかない操作 になりがちです。次の点に注意します。

- 実行した時点で確定する DB がある(Oracle / MySQL)。本番では事前に SELECT COUNT(*) で件数を確認してから打つ

- AUTO_INCREMENT がリセットされる(DB による)。ID を外部システムや URL で公開している場合、リセット後の採番衝突に注意

- 外部キー制約があると実行できないことがある(多くの DB)。subscription から customer への外部キーがあると、customer を TRUNCATE するには子テーブル側を先に空にする必要がある

- トリガーが原則起動しないトリガー(trigger)は行の追加・更新・削除をきっかけに DB が自動で実行する SQL のしくみで、たとえば「行が削除されたら別テーブルに監査ログを書く」といった処理を仕込めます。DELETE ではこのトリガーが行ごとに実行されますが、TRUNCATE は行単位で動かないため、監査ログなどの記録が残らない

速度を最優先したい大量データの一括削除で TRUNCATE を選び、それ以外は DELETE WHERE実行後に取り消せる DELETE を選ぶのが安全です。

QUIZ

理解度チェック

まずは1問ずつ答えてみましょう。

Q1DELETE と TRUNCATE の説明として正しいものはどれですか。

Q2ハイウォーターマーク (HWM) の説明として最も正しいものはどれですか。

Q3100 万行のテーブルを DELETE FROM 表;(WHERE なし)で空にした直後、SELECT COUNT(*) FROM 表; を流したときの動作として正しいものはどれですか。