◀ 4.

Oracle 19c DELETE文|削除対象の確認・COMMIT前の判断・ROLLBACKの条件

▶

Oracle 19c DELETE:対象確認から確定判断まで

DELETE は行を物理削除する DML です。本稿は Oracle Database 19c を中心に、PostgreSQL 18・MySQL 8.4 InnoDB の比較例を残しています。WHERE の指定だけでは、誤対象・連鎖削除・早すぎる確定を防げません。

Oracle 19c DELETEの対象確認、実削除結果の照合、COMMIT前の確定または取消判断を示す模式図
模式図:対象の読取確認と実削除後の照合は別段階です。取消条件を保てた未確定の処理だけが ROLLBACK の対象で、COMMIT 後や結果不明時の復旧を保証する図ではありません。
  1. 物理削除の要否を選ぶ。 表示からの除外だけなら、既存業務で定義された論理削除で足りるか確認します。物理削除が必要でも、DB・schema・接続先・実表・権限・参照関係・復旧用の退避と復元手順が不明なら、読取確認で停止します。論理削除も自動的に完全復旧できる仕組みではありません。
  2. 取消を残す接続条件を確認する。 他業務の未確定処理と混ぜず、同じ接続で作業します。Oracle 19c は DML で transaction が始まりますが、client autocommit や通常表 DDL の暗黙 COMMIT があると取消条件を失います。SQL*Plus の AUTOCOMMIT OFF と終了時の確定は別設定であり、既定の EXITCOMMIT ON では終了時に確定します。別driverやframeworkへ既定値を一般化せず、管理中の transaction に開始文や設定変更を重ねません。
  3. 件数だけでなく対象を照合する。 DELETE と同じ WHERE の SELECT/COUNT で主キー・内容・予定件数を確認します。ただし COUNT と DELETE は別文で、同時更新により集合が変わります。件数が同じでも同じ行とは限りません。対象を固定する要件があればキーの確定・競合検知・必要なロックと分離レベルを事前設計します。Oracle/PG の READ COMMITTED や InnoDB の snapshot read を、後の DELETE の対象固定とみなしません。
  4. 実削除後、COMMIT の前で分岐する。 client の実行結果の削除件数、対象の確認結果、FK/CASCADE と trigger の影響を照合します。親の削除件数は子への連鎖総数ではありません。期待する範囲と確認できた場合だけ COMMIT、不一致・エラー・未確認なら COMMIT せず、未確定の処理を ROLLBACK して原因を調べます。0件が SQL エラーとは限らず、0件だけで成功とも判断しません。
  5. 確定済み・結果不明を分ける。 COMMIT 済みの変更や外部副作用は通常の ROLLBACK で戻りません。切断で確定応答が不明なら無条件再送せず、業務キーと記録で結果を照合します。復旧・補償・再試行はその結果と業務上の処理単位を確認して判断します。

比較例の開始条件: PostgreSQL 18 は明示 BEGIN のない文が成功すると文単位で確定するため、旧 DELETE→ROLLBACK の枠だけでは取消例になりません。MySQL 8.4 InnoDB では START TRANSACTION 等で未確定範囲を管理しますが、既存 transaction 中の START TRANSACTION は先行処理を確定し得ます。Oracle の BEGIN は PL/SQL ブロック開始で、PG の transaction 開始文を転用しません。

読み方と確認範囲: 続く元の要点表・各章には比較用の旧 SQL をそのまま残し、それぞれ直前で適用条件を訂正しています。本稿の作成では Oracle の新旧 SQL および他製品の比較 SQL はすべて未実行です。一次資料との照合は実表・client・競合・復旧の動作確認ではありません。

この記事の要点
  • DELETE 文はテーブルから行を削除する DML。DELETE FROM テーブル WHERE 条件 が基本形
  • WHERE 句を忘れると全行削除される。本番作業では必ずトランザクション内で SELECT による事前確認を行うこと
  • Oracle 19c の DELETE は、クライアントによる自動確定や暗黙 COMMIT がなく未確定である場合に ROLLBACK で取消可能。接続の設定を先に確認し、対象と実削除件数を確認した後だけ COMMIT する
  • TRUNCATE は全行を除去し定義を残すが、権限・制約・取消条件が DELETE と異なる。Oracle 19c の通常表では ROLLBACK 不可、PostgreSQL 18 では未確定の明示トランザクション内なら取消可能。速度だけで選ばない

DELETE 文とは

DELETE 文は、SQL でテーブルから既存の行を削除するための DML (Data Manipulation Language) ステートメントです。Oracle、MySQL、PostgreSQL、SQL Server など主要な RDBMS で共通して使えます。

誤って実行すると業務データを失うリスクが大きいため、本記事では構文だけでなく安全に運用するための作法もまとめます。

本記事は Oracle Database 19c の DELETE を主対象に、PostgreSQL 18 と MySQL 8.4 InnoDB の相違を比較します。SQL Server の比較は通常表の TRUNCATE の取消条件に限ります。以下の旧 SQL 枠は説明資料として保持したもので、本稿の作成では SQL を実機実行していません。実表・列型・制約・トリガ・接続設定への適合や成功を保証する完成手順ではなく、そのまま本番へ貼り付けないでください。

基本構文

旧例1・構文の読み方: テーブル名と削除条件はプレースホルダです。実行用 SQL ではありません。Oracle 19c を含め実表・所有者・権限・条件を確認し、取消の接続条件と対象確認ができるまでは実行に進みません。この枠は実機未実行です。

DELETE FROM テーブル名
WHERE 削除条件;

例として、users テーブルから id = 100 の行を削除する場合は次のように書きます。

旧例2・ID 限定でも確認が必要: users と id=100 は教材上の名前と値です。実在・一意性は未確認であり、一意制約がなければ複数行に一致し得ます。同じ条件の件数と行の内容、FK とトリガの影響を調べ、同一接続で未確定に保てる条件を確認してください。実機未実行であり、本番用の完成手順ではありません。

DELETE FROM users
WHERE id = 100;

WHERE 句を忘れると全行削除される

DELETE 文で最も危険なのは、WHERE 句を忘れて全行を削除してしまうことです。次の SQL はテーブルのすべての行を削除対象とします。

旧例3・実行しない危険例: WHERE がない全行 DELETE です。制約で拒否される場合があっても保護の代わりにはなりません。autocommit が有効なら成功時に確定し、後の ROLLBACK では戻せません。挙動確認のためにも本番へ送信しないでください。この枠は実機未実行です。

-- 危険: users テーブルの全行が消える
DELETE FROM users;

本番環境では、DELETE を実行する前に必ず同じ WHERE 句で SELECT して件数を確認するのが鉄則です。ただし事前 COUNT は対象集合の固定ではありません。Oracle と PostgreSQL の READ COMMITTED では各文の参照時点が異なり得ます。MySQL InnoDB の通常 SELECT の snapshot も、後の DELETE の対象を固定しません。同時更新やトリガの影響を考慮し、事前件数と実削除件数・対象を別々に確認します。

旧例4・一括実行不可: この枠には開始条件と DELETE 後の確認分岐がなく、末尾の COMMIT を自動実行してはいけません。Oracle 19c は client autocommit がなく未確定であること、PostgreSQL は DELETE 前の BEGIN、MySQL は InnoDB を対象に START TRANSACTION が必要です。いずれも同じ接続で行い、既存の transaction 管理を二重に開始しません。Oracle 19c で last_login が DATE 型なら、境界を DATE '2020-01-01' のような型付き日付リテラルにして文字列からの NLS 依存の暗黙変換を避けます。DATE リテラルの時刻は午前0時です。TIMESTAMP・タイムゾーン付き型・文字列列では業務上の境界と列型に合う値や bind を別途設計し、文字列比較を日付比較と同一視しません。元の列型は未確認です。実削除件数と対象が期待どおりと確認できた場合だけ確定、不一致・エラー・未確認なら未確定部分を ROLLBACK する分岐を設けます。この旧枠は実機未実行です。

-- 1) 削除前: 件数確認
SELECT COUNT(*) FROM users WHERE last_login < '2020-01-01';

-- 2) 期待件数なら DELETE 実行
DELETE FROM users WHERE last_login < '2020-01-01';

-- 3) コミット
COMMIT;

トランザクションと ROLLBACK

Oracle 19c は DML でトランザクションが始まります。ただし client の自動確定、通常表 DDL による暗黙 COMMIT、終了操作による確定があれば取消条件を失います。PostgreSQL 18 では明示 BEGIN のない文も内部でトランザクションとして実行されますが、その文が成功すると確定します。後から ROLLBACK を送っても戻りません。クライアントが開始を管理する場合はその境界を確認し、未確定の変更だけを ROLLBACK の対象とします。

旧例5・Oracle と PostgreSQL を同条件にしない: 枠内の「まだコミットしていない」は無条件には正しくありません。Oracle は client autocommit なし・暗黙確定なしの同一接続が前提です。PostgreSQL はこの DELETE より前に BEGIN で開始した未確定トランザクション内でなければ取消例になりません。Oracle の BEGIN は PL/SQL ブロックの開始であり、PostgreSQL の開始文をそのまま足しません。order_id の実在や型・一意性も未確認です。この枠は実機未実行の旧例です。

-- Oracle / PostgreSQL のセッション
DELETE FROM orders WHERE order_id = 999;
-- まだコミットしていない

ROLLBACK;  -- 削除を取り消す

MySQL の InnoDB エンジンも同様にトランザクションをサポートしますが、デフォルト設定では autocommit=1 となっており、各文が即座にコミットされます。明示的にトランザクションを使う場合は START TRANSACTION で開始します。

旧例6・MySQL 8.4 InnoDB 限定: この開始から ROLLBACK までが同一接続の未確定処理で、途中に暗黙 COMMIT がない場合の例です。非トランザクション表や外部副作用まで戻る保証ではありません。既存 transaction 中の START TRANSACTION は先行処理を確定し得るので、他の業務処理や framework 管理へ重ねて実行しません。接続先・列・制約を照合し、実機未実行の枠として読んでください。

-- MySQL
START TRANSACTION;
DELETE FROM orders WHERE order_id = 999;
ROLLBACK; -- まだ COMMIT していなければ取り消せる

JOIN を使った削除

関連テーブルを参照しながら削除したい場合は、サブクエリや JOIN を使います。書き方は DBMS によって少し異なります。

Oracle / PostgreSQL (サブクエリ)

旧例7・関連先に応じた複数行削除: withdrawn の全ユーザーに一致する注文が対象で、1件限定ではありません。users と orders の列・型・参照関係と実際の主キー集合、子への連鎖を確認します。事前 SELECT と削除の間の status 変更も考慮し、Oracle は client の自動確定なし、PostgreSQL は BEGIN 済みの同一接続で未確定に保つ必要があります。この枠は実機未実行です。

-- 退会済みユーザーの注文をすべて削除
DELETE FROM orders
WHERE user_id IN (
    SELECT id FROM users WHERE status = 'withdrawn'
);

MySQL (マルチテーブル DELETE)

旧例8・MySQL 固有の JOIN 形式: この枠の削除対象は alias o の orders であり、JOIN した users 自体を削除する指定ではありません。ただし既存 FK/CASCADE やトリガの影響は別に確認します。Oracle/PostgreSQL へこの構文を転用せず、MySQL InnoDB の同一接続で開始・実削除結果の確認・確定または取消を管理します。複数表形式には単一表 DELETE の ORDER BY/LIMIT をそのまま足せません。実機未実行です。

DELETE o
FROM orders o
JOIN users u ON o.user_id = u.id
WHERE u.status = 'withdrawn';

RETURNING 句で削除内容を取得 (PostgreSQL)

PostgreSQL 18 の RETURNING は、その DELETE が実際に削除した行の指定列を返すため、対象確認に使えます。返却結果の表示だけで永続的な監査履歴が保存されるわけではありません。保存先・アクセス制限・transaction の確定結果と対応付ける設計が必要です。

旧例9・PostgreSQL 専用: BEGIN 済みの同一接続で、返却された対象と実削除件数を確認した後にだけ確定します。BEGIN がなければ返却を読んだ時点で既に文が確定し得ます。0件は SQL エラーとは限らず、事前 COUNT と同数でも同じ集合とは限りません。この RETURNING は直接の削除対象の確認であり、CASCADE 先すべての監査記録ではありません。Oracle 19c の RETURNING INTO や MySQL 用の構文と混同しないでください。日付条件・列の実在は未確認、この枠は実機未実行です。

DELETE FROM orders
WHERE order_date < '2020-01-01'
RETURNING id, user_id, total_price;

DELETE と TRUNCATE と DROP の違い

「テーブルを空にする」操作には DELETE 以外の選択肢もあります。用途に応じて選び分けましょう。

命令分類WHEREROLLBACK速度テーブル定義
DELETEDML可未確定のトランザクション等の条件下で可。COMMIT 後は通常の ROLLBACK 不可対象件数・索引・ログ・トリガ・競合に依存残る
TRUNCATEDDL不可Oracle 19c 通常表 / MySQL 8.4 は不可。PostgreSQL 18 / SQL Server 通常表は明示トランザクション内で未確定なら可全行 DELETE より効率的な場合があるが、待機や所要時間は保証しない残る
DROP TABLEDDL不可Oracle 19c 通常表は暗黙 COMMIT を伴い通常 ROLLBACK 不可。他製品は対象とトランザクション条件を個別確認依存関係・ロック・領域処理などに依存利用中のテーブル定義とデータを除去。Oracle の recycle bin 等による条件付き復旧は通常 ROLLBACK と別

全行を除去して定義を残す TRUNCATE は DELETE と権限・ロック・参照制約・取消条件が異なります。Oracle 19c の通常表と MySQL 8.4 では暗黙確定を伴い通常 ROLLBACK で取り消せません。PostgreSQL 18 の TRUNCATE は周囲のトランザクションを未確定に保てば取消可能で、ON DELETE トリガは発火しませんが ON TRUNCATE トリガは発火します。SQL Server 通常表の TRUNCATE も明示トランザクション内で取消可能です。製品横断で「DDL だから取消不能」「トリガは一切発火しない」とは言えません。参照する子が0件でも FK 定義は残るため、子を空にすれば親 TRUNCATE が必ず通るわけでもありません。エラー回避のため制約を外したり CASCADE を追加したりせず、対象と業務要件を見直してください。DELETE を選んでも確認前 COMMIT は安全策になりません。取消条件を確保し実削除結果を確認した後だけ確定します。

外部キー制約と CASCADE

子が参照する親行の DELETE は、既存 FK の参照動作と制約の状態・検査時点によって拒否されたり、定義済みの処理が行われたりします。ON DELETE CASCADE は、参照する子行も削除する設計を外部キー定義時に選択した場合の動作です。削除エラーの自動対処として既存 FK を解除・付け替えたり、子を削除したりする手順ではありません。

旧例10・定義作成と削除を分離: この枠は CREATE TABLE と親 DELETE を並べた説明であり、一括実行する手順ではありません。Oracle 19c の通常 CREATE TABLE は構文的に妥当なら失敗時でも先行 DML を確定し得るため、取消を残す DELETE の確認用 transaction に混ぜません。既存 orders を削除・上書きして準備しないでください。専用検証環境で表・列型・users の参照キー・権限等を事前に設計し、定義作成を終えた後に削除の transaction 境界を別途管理します。親1件の実削除件数だけでは連鎖した子の件数は分からないので、子の対象も別に確認します。元の大量連鎖への注意は有効です。この枠は実機未実行です。

CREATE TABLE orders (
    id INT PRIMARY KEY,
    user_id INT,
    FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);

-- users から削除すると orders 側も自動的に削除される
DELETE FROM users WHERE id = 100;

CASCADE は便利ですが、意図せず大量の子レコードを巻き込んで削除してしまう事故も起きやすいので、業務テーブルでは慎重に検討してください。

論理削除という選択肢

業務システムでは、物理的に DELETE するのではなく deleted_at や delete_flag といったカラムでマークだけする論理削除が広く採用されています。誤削除からの復旧、監査ログ、関連データの整合性維持などの観点で物理削除より柔軟です。

DELETE 文の内部動作とパフォーマンス

DELETE は行だけでなく、索引・ログ・既存トリガにも影響します。MySQL InnoDB では削除マーク後の purge と領域の再利用、OS へのファイル容量返却は別です。削除直後のファイル縮小を保証せず、領域回収のために OPTIMIZE TABLE を自動追加しません。大量 DELETE はログ量・待機・処理時間を増やす場合があるため、対象件数と索引、実行計画、同時利用、ログ容量を確認します。分割するか一括で原子性を保つかは業務上の単位で判断し、固定の数千件という値を安全保証にしません。分割して各回 COMMIT すれば、済んだ分を後の ROLLBACK で全体取消できません。夜間実行でも競合はなくならず、TRUNCATE への置換は権限・取消・参照・全行除去の条件を別に確認します。

LIMIT 付き DELETE の分割条件と取消範囲

MySQL 8.4 の単一表 DELETE には LIMIT を指定できますが、件数制限だけで短時間ロックや低負荷を保証するものではありません。繰り返しの全表走査や競合も調べます。Oracle 19c と PostgreSQL 18 の DELETE に同じ LIMIT は直接指定できません。主キーを限定する案でも製品ごとの構文、選択と削除の間の競合、対象固定方法を設計します。分割順が必要なら主キー等で同値を解消する一意な順序と再開点を決め、ORDER BY のない LIMIT を安定した順序と扱いません。各バッチで同一接続の開始・対象確認・実削除件数確認・確定または取消を行い、0件・想定外の増減・エラー・進捗不明で停止する条件を決めておきます。COMMIT 済みバッチを含む全体の取消はできません。進捗と確定結果を記録し、必要なら待機を入れつつ遅延・負荷を観測しますが、sleep だけで解消するとは保証しません。ここで新たな実行 SQL や自動削除ループは提示しません。

削除時のロックとデッドロック

DELETE は対象の競合更新を待たせるロックを取得します。範囲や継続時間は製品・索引・分離レベル等に依存し、対象行だけとは限りません。複数接続の取得順が交差するとデッドロックが起き得るため、処理間で同じ順序で行をロックする設計は有効です。ただし WHERE に id を書いたことや事前 SELECT の並びだけで実際の取得順まで保証しません。MySQL InnoDB のデッドロックは transaction 全体を取り消しますが、Oracle 19c のデッドロックで失敗した文は文単位の取消となり得て、先行する未確定処理が残ります。エラーなら全体取消済みと決めつけず、必要な処理単位を明示的に終了・確認してから、上限と待機を決めて再試行を検討します。確定済み処理や外部副作用、結果不明の要求を無条件に再送しません。

ORM での DELETE

Laravel Eloquent や Django ORM からも削除を要求できますが、生成される処理は製品・モデル設定で確認します。User::where('status', 'inactive')->delete() は Laravel Eloquent の例です。SoftDeletes なしでは物理 DELETE、SoftDeletes trait を使うモデルでは deleted_at を更新し通常のモデル検索から除外する動作になります。Laravel の forceDelete() は物理削除、withTrashed() は削除済みを含む検索です。これらは Django 共通の API ではありません。Django の delete() を標準の論理削除と扱わず、モデルと既存の実装を確認してください。論理削除も参照や監査、復旧条件の設計が必要で、呼び出し名だけでは安全性を保証できません。

監査とリカバリ

業務システムでは「いつ・誰が・どのデータを削除したか」を残すことが法令や社内ポリシーで求められるケースが多くあります。トリガで監査テーブルに削除内容を記録する、あるいはアプリケーションログに DELETE 文と実行ユーザーを残す、CDC (Change Data Capture) で変更ストリームをイベントとして配信するといった選択肢があります。万一の誤削除に備えて、定期的なバックアップとポイントインタイムリカバリを設定しておくことが、運用上の最後の防波堤になります。

関連記事

公式資料と確認範囲

2026-10-27 に以下の公式一次資料を再確認しました。資料上の条件を本文へ反映していますが、Oracle 新旧 SQL・他製品 SQL・実接続・ロック・性能・復旧は未実行・未確認です。

子ページ

子ページはありません

同階層のページ
  1. SELECT文
  2. INSERT
  3. UPDATE文
  4. DELETE文
  5. WHERE句
  6. JOIN句
  7. 集合演算子
  8. インラインビュー
  9. 副問い合わせ (サブクエリ)

最近更新/作成されたページ