要約(TL;DR)
oracle で行を削除する基本はDELETE。誤削除を避けるには、まず同じ条件でSELECT COUNT(*)を実行→SAVEPOINT→DELETE→確認→COMMIT/ROLLBACKの順に進めます。本稿は delete の基礎から安全運用までを、実機で通る手順で解説します。
テーブル作成やINSERT文についてはコチラ。
💰 【PR】Oracleエンジニアの市場価値、調べてみませんか?
Oracleのスキルは需要が高く、特定の資格や経験を持っていると年収が大幅にアップするケースがあります。まずはIT専門のエージェントで非公開求人をチェックしてみませんか?
結論・最短手順(スニペット狙い)
- 前提確認:削除対象と件数を
SELECT COUNT(*) FROM 表 WHERE 条件;で確認 - セーフティネット:
SAVEPOINT before_delete;を設定(戻し点) - 実行:
DELETE FROM 表 WHERE 条件;(件数はSQL%ROWCOUNTで確認) - 必要なら取得:削除した値が要る場合は
RETURNING ... INTO ...を使う(SQL*Plusはバインド変数) - コミット:問題なければ
COMMIT;、誤りはROLLBACK TO before_delete; - 全件削除の代替:履歴不要なら
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/UPDATE は SELECT 権限も要求され得ます。運用方針を確認してください。
手順・実装(番号付き/再現性重視)
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 UNLIMITED:USERS表領域に作成・格納できる容量の上限を実質なしにします(検証用途のため簡略)。GRANT CREATE SESSION:ログイン可能にします。CREATE TABLE/CREATE SEQUENCEはサンプル表・シーケンス作成のため。GRANT SELECT ANY TABLE:WHERE で列参照する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.deptnoはDEPT.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(*)で事前件数を確認→SAVEPOINT→DELETE。
-- 件数の事前確認
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(*)で件数を把握→SAVEPOINT→DELETEの順で安全に複数行を扱います。 COMMIT:削除結果を確定。バッチ運用では小分け実行(部門単位など)でUNDO/REDO増大を抑制。
意図と結果:dept→emp の親子関係を崩さず子から安全に削除。必要件数を事前に把握できます。
コマンド実行結果
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(*):削除前に対象件数の見取り。ビフォー・アフターで差分検証できます。SAVEPOINT:誤判明時の戻し先を確保。以降のDMLだけを取り消せます。
DELETE ... IN (SELECT ...):親の論理条件(部門名)で子表を一括削除。
COUNT(*):削除後の残件数を確認し、想定との差異をチェック。
COMMIT:結果を確定(運用ではログ取得/監査記録とセット推奨)。
ROLLBACK TO:SAVEPOINT以降のみ取り消すため、他の作業影響を最小化。
意図と結果:サブクエリで対象を限定→戻し点で安全性確保→削除→検算→確定の定石パターン。
トラブルシューティング(参照系での確認→対処)
| 症状/エラー | 主な原因 | まず確認(参照系のみ) | 参照ドキュメント |
|---|---|---|---|
ORA-02292 子レコードあり | 親を先に削除 | 子表に該当行があるか SELECT * FROM 子 WHERE 外部鍵=値; | |
| 削除できない/権限不足 | DELETE 権限不足、または SQL92_SECURITY=TRUE で SELECT も必要 | 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. 条件に関係なく全行が削除されます。戻せるよう SAVEPOINT+ROLLBACK で安全に進めるか、履歴不要なら TRUNCATE を検討してください(取り消し不可)。
Q3. 親表の行が消せない(ORA-02292)
A. 子表に参照行が残っています。子→親の順で削除するか、要件に応じて ON DELETE CASCADE 等を設計します。
Q4. 削除後の値をアプリに渡したい
A. RETURNING ... INTO ... を使います。追加の SELECT が不要で効率的です。
まとめ(要点)
SELECT COUNT(*)→SAVEPOINT→DELETE→ 検算 →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




コメント