遅延制約とは

遅延制約 (DEFERRABLE CONSTRAINT) とは、制約に違反した場合の制御を 文単位 (DML 単位)ではなく トランザクション 単位に遅延することが可能になる定義である。 遅延することで 外部参照制約 の登録順序などを気にする必要がなくなる。

遅延制約の設定は制約の定義のときに使用可能、不可能を設定する。デフォルトは即時制約(不可能設定)となっている。 この遅延不可、即時制約の設定を後から遅延制約可能に変更することはできない。

遅延制約と索引の関係

通常、主キーやユニークキー に対しての実装はユニークインデックスによって実現されていることは知られている。 では遅延制約は、主キーに対してどのようにしてユニーク制約を実装しているのかという疑問がわく。

ディクショナリを確認してみたところ、主キーやユニークキーに対して遅延制約可能で定義すると、 その実装にあたるインデックスは NONUNIQUE なインデックスで定義される。 なるほど、制約定義時にだけ遅延可能を宣言できるという制限事項にも納得できる。 裏で違う仕組みが働いているというわけなのだろう。

この実現手段の切り替えによって制約のチェック以外のトータル的なレスポンスに対してどの程度影響があるののかはわからない。 コストベースの場合、物理的(インデックス的)に NONUNIQUE であっても論理的に UNIQUE であること実行計画に加味している可能性がある。 統計情報がメンテナンスされている状態であれば実行計画には大きな変動はないとも思われる。 ルールベースの場合、その内容がほとんど変更されていない古い オプティマイザ であることから、 アクセスパス のチューニングには不安が残る。 また、(実際検証していないので憶測ですが)Oracle が舞台裏で忙しく働いている雰囲気が漂うので 安易に使用するのは避け、できれば使わない設計にする努力はしておいた方が良さそうな印象を受ける。

  • PRIMARY KEY が NONUNIQUE INDEX
-- DROP TABLE UNIQUE_TEST
CREATE TABLE UNIQUE_TEST
( ID	NUMBER )
/
ALTER TABLE UNIQUE_TEST
  ADD CONSTRAINT
  UNIQUE_TEST_PK PRIMARY KEY (ID) DEFERRABLE
/
SELECT INDEX_NAME, UNIQUENESS
  FROM USER_INDEXES
 WHERE TABLE_NAME='UNIQUE_TEST'
/
...
INDEX_NAME                 UNIQUENESS
-------------------------- ----------
UNIQUE_TEST_PK             NONUNIQUE     ← プライマリキーなのに NON-UNIQUE

遅延制約のトランザクション単位

遅延制約においては制約違反が残ったのままで コミット を行なったときには、 文単位ではなく トランザクション 単位でロールバックが行なわれる。 即時制約の場合にはロールバックは文単位(文ロールバック)となり直前の DML のみが暗黙的にロールバックされる。 これは文単位に暗黙のセーブポイント(※)が設置されていることによるものである。

(※) 暗黙のセーブポイントは ROLLBACK などで参照できない。

トランザクション単位の整合性制約を確認するには

set constraints {all|constraint_name} immediate

文を使用する。 SET CONSTRAINTS ALL IMMEDIATE を実行することでロールバックされず、例外のみが発生する。 COMMIT 前に例外処理を設けることで トランザクション全体がロールバックされるのを防ぐことが可能になる。

遅延制約の例 (表定義は上のものを使用)

SQL> SET CONSTRAINT ALL DEFERRED ;
制約が設定されました。
 
SQL> INSERT INTO UNIQUE_TEST VALUES(1);
SQL> INSERT INTO UNIQUE_TEST VALUES(1);
SQL> SET CONSTRAINT ALL IMMEDIATE ;
行1でエラーが発生しました。:
ORA-00001: 一意制約(RIVUS.UNIQUE_TEST_PK)に反しています
 
SQL> DELETE FROM UNIQUE_TEST WHERE ROWNUM = 1;
1行が削除されました。
SQL> SET CONSTRAINT ALL IMMEDIATE ;
制約が設定されました。
 
SQL> COMMIT;
コミットが完了しました。
SQL> SELECT * FROM UNIQUE_TEST;
        ID
----------
         1
 


関連事項

日本オラクル
■ 日本オラクル 株式会社
■ オラクルマスター資格 (オラクルマスターとは
■ 会員制(無料)の公式技術サイト