Oracle の制約(Constraint)はテーブルに格納するデータのルールを定義します。データの整合性を DB レベルで保証するため、アプリケーション側でのバリデーションだけに頼らず制約を適切に設定することが重要です。この記事では各制約の定義方法から、大量データ投入時の ENABLE/DISABLE、バッチ処理で使う DEFERRABLE まで解説します。
この記事でわかること
- PRIMARY KEY・UNIQUE・NOT NULL・CHECK・FOREIGN KEY の定義方法
- CREATE TABLE 時と ALTER TABLE での制約追加
- 制約の ENABLE/DISABLE・VALIDATE/NOVALIDATE
- DEFERRABLE(遅延チェック)で循環参照の INSERT を可能にする
- ON DELETE CASCADE・SET NULL による連鎖削除・NULL 設定
- USER_CONSTRAINTS・USER_CONS_COLUMNS で制約情報を確認する
制約の種類
| 制約 | 説明 | インデックス自動作成 |
|---|---|---|
| PRIMARY KEY | 一意かつ NOT NULL。テーブルに1つだけ | UNIQUE インデックスを自動作成 |
| UNIQUE | 一意性を保証(NULL は複数可) | UNIQUE インデックスを自動作成 |
| NOT NULL | NULL の禁止 | なし |
| CHECK | 列の値が条件式を満たすことを保証 | なし |
| FOREIGN KEY | 参照先テーブルの値との整合性を保証 | なし(手動で作成を推奨) |
CREATE TABLE で制約を定義する
CREATE TABLE で各制約を定義する(列制約・表制約)
-- 列制約: 列の定義の直後に記述(単一列)
-- 表制約: 列定義の後に別途記述(複合列や名前付き制約)
CREATE TABLE orders (
-- PRIMARY KEY: 1テーブルに1つだけ。自動的に NOT NULL + UNIQUE になる
order_id NUMBER CONSTRAINT pk_orders PRIMARY KEY,
-- NOT NULL: NULL 値の禁止(列制約として定義)
customer_id NUMBER NOT NULL,
-- UNIQUE: NULL は複数可(email が NULL の行は複数登録できる)
email VARCHAR2(200) CONSTRAINT uq_orders_email UNIQUE,
-- CHECK: 値の範囲チェック(status は指定した値のみ)
status VARCHAR2(20) DEFAULT 'PENDING'
CONSTRAINT chk_orders_status
CHECK (status IN ('PENDING', 'PROCESSING', 'COMPLETED', 'CANCELLED')),
order_date DATE DEFAULT SYSDATE NOT NULL,
total_amount NUMBER(12, 2) CONSTRAINT chk_amount_positive CHECK (total_amount >= 0),
-- FOREIGN KEY: 参照先テーブルの列と一致する値のみ格納可
-- ON DELETE CASCADE: 親レコード削除時に子レコードも連鎖削除
-- ON DELETE SET NULL: 親レコード削除時に FK 列を NULL に設定
--(デフォルト: 親レコードがある限り削除不可 ORA-02292)
CONSTRAINT fk_orders_customer
FOREIGN KEY (customer_id)
REFERENCES customers (customer_id)
ON DELETE SET NULL,
-- 複合 PRIMARY KEY(複数列を組み合わせた PK)は表制約として定義
-- CONSTRAINT pk_order_items PRIMARY KEY (order_id, item_seq)
);
-- 複合 UNIQUE 制約の例
CREATE TABLE product_prices (
product_id NUMBER NOT NULL,
currency CHAR(3) NOT NULL,
price NUMBER(10, 2),
CONSTRAINT uq_product_currency UNIQUE (product_id, currency) -- 組み合わせで一意
);
ALTER TABLE:制約の追加・削除・変更
ALTER TABLE で制約を追加・削除する
-- 制約を後から追加
ALTER TABLE employees
ADD CONSTRAINT chk_salary_positive CHECK (salary > 0);
ALTER TABLE order_items
ADD CONSTRAINT fk_order_items_order
FOREIGN KEY (order_id)
REFERENCES orders (order_id)
ON DELETE CASCADE;
-- NOT NULL は MODIFY を使って追加
ALTER TABLE employees
MODIFY (department_id NUMBER NOT NULL);
-- 制約の削除
ALTER TABLE employees
DROP CONSTRAINT chk_salary_positive;
-- PRIMARY KEY の削除(関連する UNIQUE インデックスも削除される)
-- CASCADE: この PK を参照している FOREIGN KEY も一緒に削除
ALTER TABLE orders DROP PRIMARY KEY CASCADE;
-- 制約の名前変更
ALTER TABLE orders
RENAME CONSTRAINT uq_orders_email TO uq_email;
ENABLE・DISABLE・VALIDATE・NOVALIDATE
大量データの一括 INSERT や DWH への初期ロード時には、制約を一時的に無効にしてデータを投入し、その後に有効化することでパフォーマンスを改善できます。
制約の ENABLE/DISABLE と VALIDATE/NOVALIDATE
-- 制約を無効化(既存データは変更しない) ALTER TABLE employees DISABLE CONSTRAINT fk_emp_dept; -- → DISABLE 後は FOREIGN KEY のチェックが行われない -- → 既存の違反データも残ったまま -- 大量データを INSERT INSERT INTO employees SELECT * FROM employees_staging; COMMIT; -- 制約を有効化(ENABLE) -- VALIDATE: 既存データも含めて制約チェックを行う(デフォルト) ALTER TABLE employees ENABLE CONSTRAINT fk_emp_dept; -- → 既存データに違反がある場合は ORA-02298 エラーになる -- NOVALIDATE: 新規データのみ制約チェックし、既存データはチェックしない ALTER TABLE employees ENABLE NOVALIDATE CONSTRAINT fk_emp_dept; -- → 既存の不整合データを許容しつつ、新しい違反データの追加を防ぐ -- 4つの組み合わせ -- ENABLE VALIDATE(デフォルト): 全データ対象・新規データも対象 -- ENABLE NOVALIDATE: 既存スキップ・新規データのみ対象 -- DISABLE VALIDATE: 既存違反不可・新規データは挿入後に制約チェックなし(稀) -- DISABLE NOVALIDATE: 制約無効(最も緩い状態) -- 制約の状態確認 SELECT constraint_name, status, validated FROM user_constraints WHERE table_name = 'EMPLOYEES';
DEFERRABLE 制約:チェックを COMMIT 時まで遅延する
通常の制約は DML 実行時に即座にチェックされます。DEFERRABLE 制約は COMMIT 時まで制約チェックを遅延させることができ、循環参照(A が B を参照し、B が A を参照する)のような場合に便利です。
DEFERRABLE 制約の定義と SET CONSTRAINTS の使い方
-- DEFERRABLE: 制約チェックを遅延可能にする
-- INITIALLY IMMEDIATE(デフォルト): 各 DML 実行時にチェック(遅延なし)
-- INITIALLY DEFERRED: デフォルトで COMMIT 時まで遅延
-- 循環参照の例: employees の manager_id は employees を参照する
CREATE TABLE emp2 (
emp_id NUMBER PRIMARY KEY,
name VARCHAR2(100),
manager_id NUMBER,
CONSTRAINT fk_emp2_manager
FOREIGN KEY (manager_id)
REFERENCES emp2 (emp_id)
DEFERRABLE INITIALLY DEFERRED -- COMMIT 時まで遅延チェック
);
-- DEFERRABLE INITIALLY DEFERRED の場合:
-- 循環データの INSERT が可能(COMMIT 時に一括チェック)
INSERT INTO emp2 VALUES (1, 'CEO', NULL);
INSERT INTO emp2 VALUES (2, 'VP', 1);
INSERT INTO emp2 VALUES (3, 'Mgr', 2);
COMMIT; -- ここでまとめてチェック(すべて整合していれば成功)
-- SET CONSTRAINTS: セッション内で一時的に遅延モードを切り替える
SET CONSTRAINTS fk_emp2_manager DEFERRED; -- 遅延モードに切り替え
SET CONSTRAINTS fk_emp2_manager IMMEDIATE; -- 即時モードに戻す(即時チェックが走る)
SET CONSTRAINTS ALL DEFERRED; -- すべての DEFERRABLE 制約を遅延に
ON DELETE CASCADE・ON DELETE SET NULL:連鎖削除の設定
ON DELETE CASCADE と ON DELETE SET NULL
-- デフォルト(ON DELETE 指定なし):
-- → 子レコードが存在する場合に親を削除しようとすると ORA-02292 エラー
-- ON DELETE CASCADE: 親を削除すると子レコードも自動削除
CREATE TABLE order_items (
order_id NUMBER,
item_seq NUMBER,
product_id NUMBER,
CONSTRAINT pk_order_items PRIMARY KEY (order_id, item_seq),
CONSTRAINT fk_oi_order
FOREIGN KEY (order_id)
REFERENCES orders (order_id)
ON DELETE CASCADE -- orders を削除すると order_items も削除
);
-- ON DELETE SET NULL: 親を削除すると子の FK 列を NULL に設定
CREATE TABLE employees (
employee_id NUMBER PRIMARY KEY,
department_id NUMBER,
CONSTRAINT fk_emp_dept
FOREIGN KEY (department_id)
REFERENCES departments (department_id)
ON DELETE SET NULL -- departments を削除すると employees.department_id が NULL になる
);
-- 確認: ON DELETE の設定は USER_CONSTRAINTS の DELETE_RULE で確認
SELECT constraint_name, delete_rule
FROM user_constraints
WHERE table_name = 'EMPLOYEES'
AND constraint_type = 'R';
-- R = Referential(FOREIGN KEY)
-- DELETE_RULE: CASCADE / SET NULL / NO ACTION
制約情報を確認するデータディクショナリ
USER_CONSTRAINTS・USER_CONS_COLUMNS で制約を確認する
-- 制約の一覧確認
SELECT
constraint_name,
constraint_type, -- P=PK, U=UNIQUE, C=CHECK/NOT NULL, R=FK
status, -- ENABLED / DISABLED
validated, -- VALIDATED / NOT VALIDATED
rely -- RELY(NOVALIDATE でも最適化ヒントに使う)
FROM user_constraints
WHERE table_name = 'ORDERS'
ORDER BY constraint_type;
-- 制約を構成する列の確認
SELECT
uc.constraint_name,
uc.constraint_type,
ucc.column_name,
ucc.position
FROM user_constraints uc
JOIN user_cons_columns ucc
ON uc.constraint_name = ucc.constraint_name
AND uc.owner = ucc.owner
WHERE uc.table_name = 'ORDERS'
ORDER BY uc.constraint_type, ucc.position;
-- FOREIGN KEY の参照先確認
SELECT
uc.constraint_name,
uc.r_constraint_name, -- 参照先制約名
(SELECT table_name FROM user_constraints
WHERE constraint_name = uc.r_constraint_name) AS ref_table,
uc.delete_rule
FROM user_constraints uc
WHERE uc.table_name = 'ORDER_ITEMS'
AND uc.constraint_type = 'R';
まとめ
- PRIMARY KEY:自動的に NOT NULL + UNIQUE。UNIQUE インデックスを自動作成
- FOREIGN KEY:ON DELETE CASCADE(連鎖削除)または SET NULL を指定可。FK 列にはインデックスを手動作成することを推奨(JOIN 性能改善)
- ENABLE/DISABLE:大量ロード時は無効化してパフォーマンス改善。再有効化時に NOVALIDATE で既存データをスキップ可
- DEFERRABLE:COMMIT 時まで制約チェックを遅延。循環参照の INSERT や複雑なバッチ処理に活用
- ON DELETE CASCADE:親削除時に子も自動削除。ただし意図しない大量削除に注意
FOREIGN KEY 違反エラー(ORA-02291・ORA-02292)の原因と対処はORA-02291: 親キーがありません の原因と解決方法完全ガイドを、インデックスの作成方法・設計のポイントはインデックス完全ガイドも参照してください。

