Oracle SQL DELETE入門:安全な削除手順と実行例【19c】

Oracle Master Bronze

要約(TL;DR)
oracle で行を削除する基本は DELETE。誤削除を避けるには、まず同じ条件で SELECT COUNT(*) を実行→SAVEPOINTDELETE→確認→COMMIT/ROLLBACK の順に進めます。本稿は delete の基礎から安全運用までを、実機で通る手順で解説します。


テーブル作成やINSERT文についてはコチラ。

💰 【PR】Oracleエンジニアの市場価値、調べてみませんか?

Oracleのスキルは需要が高く、特定の資格や経験を持っていると年収が大幅にアップするケースがあります。まずはIT専門のエージェントで非公開求人をチェックしてみませんか?


結論・最短手順(スニペット狙い)

  1. 前提確認:削除対象と件数を SELECT COUNT(*) FROM 表 WHERE 条件; で確認
  2. セーフティネットSAVEPOINT before_delete; を設定(戻し点)
  3. 実行DELETE FROM 表 WHERE 条件;(件数は SQL%ROWCOUNT で確認)
  4. 必要なら取得:削除した値が要る場合は RETURNING ... INTO ... を使う(SQL*Plusはバインド変数)
  5. コミット:問題なければ COMMIT;、誤りは ROLLBACK TO before_delete;
  6. 全件削除の代替:履歴不要なら TRUNCATE TABLE も検討(ロールバック不可)

DELETEとは?(用語と仕組みの整理)

  • DELETE:条件に合致するを削除するDML。所有する表、または表に対する DELETE 権限が必要です。WHERE を省くと全件削除になります。19cのSQLリファレンスにもとづく一般仕様です。
  • RETURNING句:削除した行の値を即座に変数へ返せます。後続の SELECT を省略でき効率的です(PL/SQL/バインド変数で利用)。
  • SAVEPOINT:同一トランザクション内の“戻し点”。ROLLBACK TO で保存点以降だけを取り消せます。

一口メモ:DELETEはUNDO/REDOを発生させる行単位操作。大量削除ではUNDOセグメント圧迫やログ量に留意します(後述の運用上の注意を参照)。


まず前提(実行環境と権限)

  • 対象:Oracle Database 19c(非CDB)、OSは Linux
  • 接続sqlplus / as sysdba(ユーザー作成のみSYSで実施)
  • 権限:サンプルユーザーに CREATE SESSION / CREATE TABLE / CREATE SEQUENCE を付与(最小限)
  • 注意:12.2以降は初期化パラメータ SQL92_SECURITY=TRUE が推奨/環境によっては既定で有効。WHERE で列参照する DELETE/UPDATESELECT 権限も要求され得ます。運用方針を確認してください。

手順・実装(番号付き/再現性重視)

1) サンプルユーザーと表の用意(SYSで一度だけ)

目的:検証専用ユーザー APP_USER と、親子関係を持つ DEPT(親)/EMP(子)を作成します。誤削除検証や ORA-02292 の再現に使います。

-- SYSで実行(Linux・非CDB想定)
sqlplus / as sysdba

-- 1-1. ユーザー作成
CREATE USER app_user IDENTIFIED BY "AppPass1"
  DEFAULT TABLESPACE users
  TEMPORARY TABLESPACE temp
  QUOTA UNLIMITED ON users;

-- 1-2. 必要最小限の権限
GRANT CREATE SESSION, CREATE TABLE, CREATE SEQUENCE TO app_user;

-- SQL92_SECURITY=TRUE 環境では、WHEREで列参照するDELETE/UPDATEにSELECT権限が要る
GRANT SELECT ANY TABLE TO app_user; -- ※最小化したい場合は対象表に対するSELECTを個別に付与

補足(このSQLの意味)

  • CREATE USER ... QUOTA UNLIMITEDUSERS 表領域に作成・格納できる容量の上限を実質なしにします(検証用途のため簡略)。
  • GRANT CREATE SESSION:ログイン可能にします。CREATE TABLE / CREATE SEQUENCE はサンプル表・シーケンス作成のため。
  • GRANT SELECT ANY TABLEWHERE で列参照するDELETE/UPDATEに必要となる場合がある SELECT 権限を簡便に満たします(本番は個別表単位の付与を推奨)。
  • sqlplus / as sysdba:OS認証でSYSDBAとして接続(ユーザー作成は管理者権限で一度だけ実施)。
-- APP_USERで接続
conn app_user/AppPass1

-- 1-3. 親子テーブル作成(DEPT←EMP)
CREATE TABLE dept (
  deptno NUMBER PRIMARY KEY,
  dname  VARCHAR2(30)
);

CREATE TABLE emp (
  empno   NUMBER PRIMARY KEY,
  ename   VARCHAR2(30) NOT NULL,
  deptno  NUMBER NOT NULL,
  CONSTRAINT emp_fk_dept FOREIGN KEY (deptno) REFERENCES dept(deptno)
);

-- 1-4. データ投入
INSERT INTO dept VALUES (10, 'ACCOUNTING');
INSERT INTO dept VALUES (20, 'SALES');

INSERT INTO emp VALUES (7369, 'SMITH', 20);
INSERT INTO emp VALUES (7499, 'ALLEN', 20);
INSERT INTO emp VALUES (7902, 'FORD', 10);
COMMIT;

補足(このSQLの意味)

  • PRIMARY KEY:一意性+NOT NULLを保証。主キー索引が作成され、主キー指定の検索やDELETEが高速化します。
  • FOREIGN KEY ... REFERENCES dept(deptno)EMP.deptnoDEPT.deptno 既存値のみ許容。参照整合性を担保し、親の誤削除を防ぎます。
  • INSERT ...; COMMIT;:サンプルデータを確定。以後のDELETE例で実際に影響が出るようにします。
コマンド実行結果

SQL> SELECT * FROM dept;

DEPTNO DNAME
---------- ------------------------------
10 ACCOUNTING
20 SALES

SQL> SELECT * FROM emp;

EMPNO ENAME DEPTNO
---------- ------------------------------ ----------
7369 SMITH 20
7499 ALLEN 20
7902 FORD 10

テキスト図(処理の流れ・安全版)(alt: Oracle delete の安全な実行手順フロー

┌───────── 事前確認 ─────────┐
│ SELECT COUNT(*) WHERE 条件     │  ← 件数と条件を確定
└───────┬──────────────────┘
        │
┌───────▼─────────┐
│ SAVEPOINT before_delete │  ← 戻し点を作る
└───────┬─────────┘
        │
┌───────▼───────────────┐
│ DELETE FROM 表 WHERE 条件 │  ← 削除を実行
└───────┬───────────────┘
        │
┌───────▼───────────────┐
│ SELECT COUNT(*) で確認   │  ← 想定どおり?
└───────┬───────────────┘
     はい│いいえ
        │
   ┌────▼────┐         ┌───────────────▼──────────────┐
   │ COMMIT │         │ ROLLBACK TO before_delete │
   └────────┘         └──────────────────────────┘

2) 基本のDELETE(WHEREで1行削除)

説明:特定の社員(主キー)の行を1件削除。SELECT COUNT(*) で事前件数を確認→SAVEPOINTDELETE

-- 件数の事前確認
SELECT COUNT(*) AS cnt FROM emp WHERE empno = 7369;

SAVEPOINT before_delete_emp;

DELETE FROM emp WHERE empno = 7369;
-- このセッションで直近のDML件数は SQL%ROWCOUNT で確認(PL/SQL/SQL*Plus置換変数で可)

COMMIT;

補足(このSQLの意味)

  • SELECT COUNT(*) ...削除対象の行数を可視化して誤条件を検出(0件や想定外の複数件を早期に気付く)。
  • SAVEPOINT before_delete_emp:この時点を戻し点としてマーク。以降の誤操作は ROLLBACK TO でここまで戻せます。
  • DELETE FROM emp WHERE empno = 7369:主キー一致で1行のみ削除。主キー索引利用で効率的。
  • COMMIT:確定。逆に確信が持てない場合は ROLLBACK で取り消します。

意図と結果:主キー指定なのでインデックスが使われ高速。1件だけ削除され、他行に影響しません。

SAVEPOINT についてはコチラ。

コマンド実行結果

SQL> SELECT COUNT(*) AS cnt FROM emp WHERE empno = 7369;

CNT
----------
1

SQL> SAVEPOINT before_delete_emp;

Savepoint created.

SQL> DELETE FROM emp WHERE empno = 7369;

1 row deleted.

SQL> SELECT COUNT(*) AS cnt FROM emp WHERE empno = 7369;

CNT
----------
0

SQL> COMMIT;

Commit complete.

💰 【PR】Oracleエンジニアの市場価値、調べてみませんか?

Oracleのスキルは需要が高く、特定の資格や経験を持っていると年収が大幅にアップするケースがあります。まずはIT専門のエージェントで非公開求人をチェックしてみませんか?


3) 条件付きの複数行DELETE(サブクエリ活用)

説明:部門名で子表を削除したいときは、IN (SELECT ...) などの相関サブクエリを使用します。

-- 例:部門名が'SALES' の部門に所属する社員をまとめて削除
SELECT COUNT(*) FROM emp
 WHERE deptno IN (SELECT deptno FROM dept WHERE dname = 'SALES');

SAVEPOINT before_delete_sales;

DELETE FROM emp
 WHERE deptno IN (SELECT deptno FROM dept WHERE dname = 'SALES');

COMMIT;

補足(このSQLの意味)

  • deptno IN (SELECT ...)親表の条件(部門名)で子表を絞り込み。結合DELETE構文がないOracleではサブクエリ/EXISTSが定石。
  • 一度 COUNT(*) で件数を把握→SAVEPOINTDELETE の順で安全に複数行を扱います。
  • COMMIT:削除結果を確定。バッチ運用では小分け実行(部門単位など)でUNDO/REDO増大を抑制。

意図と結果deptemp親子関係を崩さず子から安全に削除。必要件数を事前に把握できます。

コマンド実行結果

SQL> SELECT COUNT(*) FROM emp
2 WHERE deptno IN (SELECT deptno FROM dept WHERE dname = 'SALES');

COUNT(*)
----------
1

SQL> SAVEPOINT before_delete_sales;

Savepoint created.

SQL> DELETE FROM emp
2 WHERE deptno IN (SELECT deptno FROM dept WHERE dname = 'SALES');

1 row deleted.

SQL> SELECT COUNT(*) FROM emp
2 WHERE deptno IN (SELECT deptno FROM dept WHERE dname = 'SALES');

COUNT(*)
----------
0

SQL> COMMIT;

Commit complete.

4) 親行DELETEで起こりがちなエラー(ORA-02292)

説明:子行が残っている親行を消すと ORA-02292(子レコード存在)になります。

-- 子が残るパターンを作る
INSERT INTO emp VALUES (7788, 'SCOTT', 10);
COMMIT;

-- 親を直接消そうとすると…
DELETE FROM dept WHERE deptno = 10;
-- ORA-02292: integrity constraint (...) violated - child record found

補足(このSQLの意味)

  • INSERT ...; COMMIT;DEPT(10) に紐づく子 EMP を追加し整合性を成立させた状態にします。
  • DELETE FROM dept WHERE deptno = 10子が存在するため外部キー制約が発火し、親削除を拒否します。
  • 対策は子→親の順で削除(または設計で ON DELETE CASCADE を採用)。

原因と対処:外部キーにより参照整合性が守られています。子を先に削除するか、設計上許容するなら一時的に制約無効化/ON DELETE CASCADE を検討します(原則は子→親の順)。

コマンド実行結果

SQL> INSERT INTO emp VALUES (7788, 'SCOTT', 10);

1 row created.

SQL> COMMIT;

Commit complete.

SQL> DELETE FROM dept WHERE deptno = 10; ★エラーで削除が失敗する
DELETE FROM dept WHERE deptno = 10
*
ERROR at line 1:
ORA-02292: integrity constraint (APP_USER.EMP_FK_DEPT) violated - child record found

5) RETURNING句で削除した値を受け取る

説明:削除した主キーや名前などを追加のSELECTなしで取得できます(SQL*Plusはバインド変数で受け取り)。

-- バインド変数を宣言(SQL*Plus)
VAR v_empno NUMBER
VAR v_ename VARCHAR2(30)

-- 指定行を削除しつつ、値を受け取る
DELETE FROM emp
 WHERE empno = 7499
 RETURNING empno, ename INTO :v_empno, :v_ename;

PRINT v_empno
PRINT v_ename

ROLLBACK; -- デモなので戻す

補足(このSQLの意味)

  • VAR v_empno NUMBER 等:SQL*Plusのホスト変数を宣言。RETURNING ... INTO で値を格納します。
  • RETURNING empno, ename INTO ...:DELETE対象の列値を同時取得。ネットワーク往復や追加SELECTを削減できます。
  • PRINT:バインド変数の内容を表示。デモなので最後に ROLLBACK で復元しています。

意図と結果:アプリ連携や監査ログへの転記に便利。1回の往復で削除と値取得が完了します。


6) 全件削除はDELETEかTRUNCATEか

方式速度ロールバックトリガ/整合性用途目安
DELETE(WHEREなし)遅め可能トリガ発火/制約尊重履歴保持や監査が必要
TRUNCATE TABLE速い不可一部制約制限/最小ログテーブルを初期化したい時

注意TRUNCATE取り消し不可。誤操作対策としては、権限分離や本番での実行禁止が基本です。


実行例

目的:SALES 部門の社員を削除し、誤り時はROLLBACK TOで戻す。

-- 0) 事前確認
SELECT COUNT(*) AS c FROM emp
 WHERE deptno IN (SELECT deptno FROM dept WHERE dname = 'SALES');

-- 1) 戻し点
SAVEPOINT before_delete_sales;

-- 2) 削除
DELETE FROM emp
 WHERE deptno IN (SELECT deptno FROM dept WHERE dname = 'SALES');

-- 3) 検算
SELECT COUNT(*) AS after_c FROM emp
 WHERE deptno IN (SELECT deptno FROM dept WHERE dname = 'SALES');

-- 4) 問題なければ確定
COMMIT;

-- 5) 誤削除だった場合(例)
-- ROLLBACK TO before_delete_sales;

補足(このSQLの意味)

  • 0) COUNT(*):削除前に対象件数の見取り。ビフォー・アフターで差分検証できます。
    1. SAVEPOINT誤判明時の戻し先を確保。以降のDMLだけを取り消せます。
    1. DELETE ... IN (SELECT ...)親の論理条件(部門名)で子表を一括削除
    1. COUNT(*):削除後の残件数を確認し、想定との差異をチェック。
    1. COMMIT:結果を確定(運用ではログ取得/監査記録とセット推奨)。
    1. ROLLBACK TOSAVEPOINT以降のみ取り消すため、他の作業影響を最小化。

意図と結果:サブクエリで対象を限定→戻し点で安全性確保→削除→検算→確定の定石パターン


トラブルシューティング(参照系での確認→対処)

症状/エラー主な原因まず確認(参照系のみ)参照ドキュメント
ORA-02292 子レコードあり親を先に削除子表に該当行があるか SELECT * FROM 子 WHERE 外部鍵=値;
削除できない/権限不足DELETE 権限不足、または SQL92_SECURITY=TRUESELECT も必要USER_TAB_PRIVS / SESSION_PARAMETERS で権限と SQL92_SECURITY を確認
全件削除の取り消し不可TRUNCATE を使っていた実施前に方式を確認。TRUNCATE はロールバック不可
期待以上の件数を削除WHERE の条件ミス事前に SELECT COUNT(*)/サンプル SELECT * で対象を確認
パフォーマンス低下対象件数が多い、索引未整備、外部キーなしの大表対象件数と実行計画、索引有無を確認(必要ならバッチ削除や分割)

運用・監視・セキュリティの注意

  • 誤削除対策:本番は必ず事前カウントSAVEPOINT→削除→検算→COMMIT
  • 権限の最小化DELETE 権限は必要表のみに。SQL92_SECURITY=TRUE 環境ではSELECT権限の追加が必要になり得るため、役割設計で絞り込みます。
  • 大量削除:UNDO/REDOが増えるため、分割バッチ(例:主キー範囲や日時で小分け)や業務時間外の実行を検討。
  • 監査RETURNING で削除値をアプリ側ログに残すと追跡性が向上します。
  • 全件初期化:テーブル初期化は TRUNCATE を優先(ただし取り消せない)。運用標準に明記。

FAQ

Q1. DELETE にも権限が要るの?
A. はい。表の所有者か、その表への DELETE 権限が必要です。ビュー/マテビュー経由でも基本は同様です。

Q2. WHERE を省略するとどうなる?
A. 条件に関係なく全行が削除されます。戻せるよう SAVEPOINTROLLBACK で安全に進めるか、履歴不要なら TRUNCATE を検討してください(取り消し不可)。

Q3. 親表の行が消せない(ORA-02292)
A. 子表に参照行が残っています。子→親の順で削除するか、要件に応じて ON DELETE CASCADE 等を設計します。

Q4. 削除後の値をアプリに渡したい
A. RETURNING ... INTO ... を使います。追加の SELECT が不要で効率的です。


まとめ(要点)

  • SELECT COUNT(*)SAVEPOINTDELETE → 検算 → COMMIT/ROLLBACK安全な定石
  • 子行がある親行の削除は ORA-02292子→親の順に。
  • 削除値の受け取りは RETURNING が便利。
  • 全件初期化は TRUNCATEロールバック不可)の特性を理解して使い分ける。
  • 12.2以降は SQL92_SECURITY に注意。SELECT権限が追加で要る場合あり。

参考テキスト図(親子関係と削除の順序)

(alt: Oracle delete と外部キー参照の図

親表 DEPT(deptno PK)      子表 EMP(deptno FK)
┌─────┐                 ┌─────────┐
│  10  │◀───────────────│  ...10   │
│  20  │                 │  ...20   │
└─────┘                 └─────────┘

NG:DEPT(10) → 先に削除 ⇒ ORA-02292
OK:EMP(deptno=10) → 先に削除 → その後 DEPT(10)


[参考]
DELETE

💰 【PR】Oracleエンジニアの市場価値、調べてみませんか?

Oracleのスキルは需要が高く、特定の資格や経験を持っていると年収が大幅にアップするケースがあります。まずはIT専門のエージェントで非公開求人をチェックしてみませんか?

コメント

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