ORA-02291: integrity constraint (スキーマ.制約名) violated – parent key not found は、外部キー(FOREIGN KEY)制約が定義されたテーブルにINSERTまたはUPDATEしようとしたとき、参照先の親テーブルに対応する値が存在しない場合に発生します。データ移行やバッチ処理で頻繁に遭遇するエラーです。本記事では制約の特定方法から、CASCADE設定やDEFERRABLEによるロード時の回避策まで体系的に解説します。
- ORA-02291 の発生メカニズム(外部キー制約の仕組み)
- エラーメッセージから制約名・親テーブル・参照列を特定する方法
- 親テーブルに存在しない値(不整合データ)を検出するSQL
- ON DELETE CASCADE / ON DELETE SET NULL の使い方
- DEFERRABLE INITIALLY DEFERRED で大量ロード時に一括検証する方法
- 外部キー制約を一時的に無効化する方法(DISABLE / ENABLE)
- ORA-02292(子レコードが見つかりました)との違い
ORA-02291 の発生メカニズム
外部キー(FK)制約は「子テーブルの値は、必ず親テーブルに存在しなければならない」という参照整合性を保証します。この制約に違反するINSERTまたはUPDATEを行うとORA-02291が発生します。
-- 親テーブル
CREATE TABLE departments (
department_id NUMBER PRIMARY KEY,
department_name VARCHAR2(100)
);
INSERT INTO departments VALUES (10, '営業部');
INSERT INTO departments VALUES (20, '開発部');
-- 子テーブル(FK制約あり)
CREATE TABLE employees (
employee_id NUMBER PRIMARY KEY,
name VARCHAR2(100),
department_id NUMBER,
CONSTRAINT fk_emp_dept FOREIGN KEY (department_id)
REFERENCES departments (department_id)
);
-- 存在する部門IDで INSERT → 成功
INSERT INTO employees VALUES (1, '佐藤太郎', 10);
-- 存在しない部門IDで INSERT → ORA-02291
INSERT INTO employees VALUES (2, '鈴木花子', 99);
-- → ORA-02291: integrity constraint (HR.FK_EMP_DEPT) violated - parent key not found
-- → departments テーブルに department_id = 99 が存在しないため
制約名から親テーブル・参照列を特定する
エラーメッセージに含まれる制約名(例: HR.FK_EMP_DEPT)から、どのテーブルのどの列が関係しているかを調べます。
-- 制約名から子テーブル・親テーブル・列名を特定
SELECT
c.constraint_name,
c.table_name AS child_table, -- 子テーブル(FK側)
cc.column_name AS child_column, -- 子テーブルの列
c.r_constraint_name AS parent_constraint, -- 参照先の制約名
rc.table_name AS parent_table, -- 親テーブル(参照先)
rcc.column_name AS parent_column -- 親テーブルの列
FROM user_constraints c
JOIN user_cons_columns cc ON c.constraint_name = cc.constraint_name
JOIN user_constraints rc ON c.r_constraint_name = rc.constraint_name
JOIN user_cons_columns rcc ON rc.constraint_name = rcc.constraint_name
AND cc.position = rcc.position
WHERE c.constraint_name = 'FK_EMP_DEPT'
ORDER BY cc.position;
親テーブルに存在しない値を検出する
データ移行やバッチ処理で「どの行が制約に違反しているか」を事前に確認するSQLです。
-- 親テーブル(departments)に存在しない department_id を持つ子行を検出
SELECT e.employee_id, e.name, e.department_id
FROM staging_employees e
WHERE NOT EXISTS (
SELECT 1 FROM departments d
WHERE d.department_id = e.department_id
);
-- LEFT JOIN でも同じ結果が得られる
SELECT e.employee_id, e.name, e.department_id
FROM staging_employees e
LEFT JOIN departments d ON e.department_id = d.department_id
WHERE d.department_id IS NULL
AND e.department_id IS NOT NULL; -- NULLはFK違反にはならない
外部キー列がNULLの場合、ORA-02291は発生しません。NULLは「参照先が不明」という意味であり、「存在しない値を参照している」とは異なるためです。NULLを許可したくない場合は、FK列に
NOT NULL 制約を別途追加してください。
INSERT時の対処法
親テーブルに先にデータを投入する
最も基本的な対処法です。外部キー制約がある場合は親テーブル → 子テーブルの順でINSERTします。
-- ステップ 1: 親テーブル(departments)にデータを投入 INSERT INTO departments VALUES (99, '新規事業部'); -- ステップ 2: 子テーブル(employees)にデータを投入 INSERT INTO employees VALUES (2, '鈴木花子', 99); -- → 親テーブルに department_id=99 が存在するので成功
親テーブルに存在する値だけ投入する
INSERT INTO employees (employee_id, name, department_id)
SELECT employee_id, name, department_id
FROM staging_employees s
WHERE EXISTS (
SELECT 1 FROM departments d
WHERE d.department_id = s.department_id
);
-- 親テーブルに対応するdepartment_idが存在する行だけ投入される
ON DELETE CASCADE / ON DELETE SET NULL
外部キー制約のON DELETEオプションは、親テーブルの行が削除されたときの子テーブルの挙動を定義します。なお、ORA-02291はINSERT/UPDATE時のエラーであり、ON DELETEの設定はDELETE時の挙動にのみ影響します。関連するORA-02292(子レコードが見つかりました)を防ぐ設定として知っておく価値があります。
| オプション | 親行DELETE時の動作 | 用途 |
|---|---|---|
| (指定なし / デフォルト) | 子行が存在する場合、親行の DELETE を拒否する(ORA-02292) | データの整合性を厳格に保ちたい場合 |
ON DELETE CASCADE |
親行と一緒に子行も自動的に削除される | 親に従属するデータ(注文明細・ログ等) |
ON DELETE SET NULL |
子行のFK列がNULLに更新される | 親が削除されても子のデータは残したい場合 |
-- CASCADE: 親行削除時に子行も自動削除
CREATE TABLE order_items (
item_id NUMBER PRIMARY KEY,
order_id NUMBER,
product VARCHAR2(100),
CONSTRAINT fk_items_orders FOREIGN KEY (order_id)
REFERENCES orders (order_id)
ON DELETE CASCADE
);
-- orders の行を DELETE すると order_items の対応行も自動削除される
-- SET NULL: 親行削除時に子行のFK列をNULLにする
CREATE TABLE employees (
employee_id NUMBER PRIMARY KEY,
name VARCHAR2(100),
department_id NUMBER,
CONSTRAINT fk_emp_dept FOREIGN KEY (department_id)
REFERENCES departments (department_id)
ON DELETE SET NULL
);
-- departments の行を DELETE すると employees.department_id が NULL になる
DEFERRABLE 制約で大量ロード時に一括検証する
通常、FK制約はINSERT/UPDATE実行の即座にチェックされます。大量データのロードで投入順序を制御しにくい場合、DEFERRABLE INITIALLY DEFERRED を使うとCOMMIT時にまとめてチェックできます。
-- DEFERRABLE な外部キー制約を定義する
ALTER TABLE employees
ADD CONSTRAINT fk_emp_dept FOREIGN KEY (department_id)
REFERENCES departments (department_id)
DEFERRABLE INITIALLY DEFERRED;
-- 投入順序を気にせずINSERTできる
INSERT INTO employees VALUES (10, '新入社員', 99); -- department_id=99 はまだ存在しない
INSERT INTO departments VALUES (99, '新規事業部'); -- ここで親データを投入
COMMIT; -- ★ この時点で FK 整合性がチェックされる → 99 は存在するので成功
-- 現在のセッションで全ての DEFERRABLE 制約を遅延モードにする SET CONSTRAINTS ALL DEFERRED; -- 特定の制約だけ即時チェックに戻す SET CONSTRAINTS fk_emp_dept IMMEDIATE;
外部キー制約を一時的に無効化する
データ移行等で一時的に制約チェックを外したい場合、DISABLE / ENABLE が使えます。
-- 制約を無効化する(FK チェックが行われなくなる) ALTER TABLE employees DISABLE CONSTRAINT fk_emp_dept; -- データ投入(順序を気にせず自由に INSERT できる) INSERT INTO employees VALUES (5, '田中一郎', 999); -- → FK チェックが無効なのでエラーにならない(不整合データが入る可能性あり) -- 制約を再有効化する(既存データの整合性チェックあり) ALTER TABLE employees ENABLE CONSTRAINT fk_emp_dept; -- → 既存データに不整合があると ORA-02298: 親キーが見つかりません が発生して有効化に失敗する
-- 既存データはチェックせず、今後の DML からチェックを再開する ALTER TABLE employees ENABLE NOVALIDATE CONSTRAINT fk_emp_dept; -- → 既存の不整合データはそのまま残るが、以降の INSERT/UPDATE はチェックされる
制約を無効化している間は不整合データが投入される可能性があります。再有効化時に
ENABLE VALIDATE(デフォルト)でエラーが出る場合は、先に不整合データを修正してください。ENABLE NOVALIDATE は既存データをチェックしないため、不整合を見逃すリスクがあります。
関連エラー:ORA-02292(子レコードが見つかりました)
ORA-02291 と対になるエラーです。親テーブルの行を DELETE しようとしたとき、子テーブルに参照している行が存在すると発生します。
| エラー | 操作 | 意味 |
|---|---|---|
| ORA-02291 | 子テーブルへの INSERT / UPDATE | 参照先の親キーが存在しない |
| ORA-02292 | 親テーブルからの DELETE / UPDATE | 子テーブルに参照している行が存在する |
-- 子テーブルの行を先に削除してから親テーブルの行を削除する DELETE FROM employees WHERE department_id = 10; DELETE FROM departments WHERE department_id = 10; -- または ON DELETE CASCADE を設定しておけば自動削除される
テーブルの外部キー制約を一覧で確認する
SELECT
c.constraint_name,
cc.column_name AS child_column,
rc.table_name AS parent_table,
rcc.column_name AS parent_column,
c.status, -- ENABLED / DISABLED
c.validated, -- VALIDATED / NOT VALIDATED
c.delete_rule -- CASCADE / SET NULL / NO ACTION
FROM user_constraints c
JOIN user_cons_columns cc ON c.constraint_name = cc.constraint_name
JOIN user_constraints rc ON c.r_constraint_name = rc.constraint_name
JOIN user_cons_columns rcc ON rc.constraint_name = rcc.constraint_name
AND cc.position = rcc.position
WHERE c.constraint_type = 'R' -- R = Referential (外部キー)
AND c.table_name = 'EMPLOYEES'
ORDER BY c.constraint_name, cc.position;
まとめ
ORA-02291 は外部キー制約によって参照整合性が保証されている証拠です。エラーを無闇に回避するのではなく、データの整合性を保つ方向で対処するのが原則です。
- エラーメッセージの制約名から
USER_CONSTRAINTSで親テーブル・列を特定する NOT EXISTSで親テーブルに存在しない不整合データを事前に検出する- INSERT は親テーブル → 子テーブルの順序で行う
- 削除時にORA-02292が出る場合は
ON DELETE CASCADE/ON DELETE SET NULLを検討する - 大量ロード時は
DEFERRABLE INITIALLY DEFERREDでCOMMIT時に一括検証できる - データ移行時の一時的な回避には
DISABLE CONSTRAINT→ ロード →ENABLE CONSTRAINTのパターンが使える - FK列のNULLは制約違反にならない。NULLを禁止するには
NOT NULLを別途追加する

