Oracle約束constraint是我們經常使用的壹種數據庫規則對象。constraint在數據庫中的作用就是從靜態角度對數據完整性進行維護。我們經常使用的主鍵primary key和外鍵foreign key,本質上就是約束的壹種形式。
對Oracle的約束,我們有三個屬性可以進行設置,分別為deferrable、deferred和validated。針對不同的需求設計場景,采用不同類型的屬性,可以幫助我們實現不同的約束效果。下面我們分別來進行實驗。
1、 環境準備
首先我們還是準備數據實驗環境。
SQL> create table t (id number);
Table created
SQL> alter table T
2 add constraint c_t_id1
3 check (id>5);
Table altered
我們創建了數據表T,在列id上添加了約束c_t_id1。約束內容很簡單,就是要求id值保證是大於5。約束c_t_id1使用的是默認選項,數據字典中對該約束的表示如下:
SQL> select constraint_name, constraint_type ctype, SEARCH_CONDITION cond, STATUS, DEFERRABLE, DEFERRED, VALIDATED from dba_constraints where table_name='T' and wner='SYS';
CONSTRAINT_NAME CTYPE COND STATUS DEFERRABLE DEFERRED VALIDATED
-------------------- ----- ---------- -------- -------------- --------- -------------
C_T_ID1 C id>5 ENABLED NOT DEFERRABLE IMMEDIATE VALIDATED
註意,此時約束的三個屬性取值分別為:deferrable: not deferrable;deferred:immediate;validated:validated;
我們觀察壹下此時數據表的插入現象:
SQL> insert into t values (1); //插入非法的數據;
insert into t values (1)
ORA-02290: 違反檢查約束條件 (SYS.C_T_ID1) //立刻報錯,將數據剔除!
SQL> insert into t values (6);
1 row inserted
SQL> commit;
Commit complete
SQL> select * from t;
ID
----------
6
結論:在默認情況下,Oracle的約束是不允許延遲(not deferrable)、立即應用和驗證的(immediate、validated)。在數據變化的時候,立即進行約束驗證。
2、 deferrable:約束應用可以延遲
deferrable默認值為not deferrable,字面含義是不可延遲。那麽我們如果設置可以延遲,效果是什麽呢?
SQL> alter table T
2 drop constraint C_T_ID1;
Table altered
SQL> alter table T
2 add constraint C_T_ID1
3 check (id>5)
4 deferrable;
Table altered
此時,數據字典中的情況是如下:
SQL> select constraint_name, SEARCH_CONDITION cond, STATUS, DEFERRABLE, DEFERRED, VALIDATED from dba_constraints where table_name='T' and wner='SYS';
CONSTRAINT_NAME CTYPE STATUS DEFERRABLE DEFERRED VALIDATED
-------------------- ----- -------- -------------- --------- -------------
C_T_ID1 C ENABLED DEFERRABLE IMMEDIATE VALIDATED
與默認情況相比,deferrable屬性變化為了deferrable。我們觀察壹下現象:
SQL> insert into t values (3);
insert into t values (3)
ORA-02290: 違反檢查約束條件 (SYS.C_T_ID1)
在插入數據的時候,立即進行約束驗證。和默認情況下沒有差異。那麽怎麽處理呢?
//手工設置deferred屬性為deferred
SQL> set constraint c_t_id1 deferred;
Constraints set
SQL> insert into t values (3); //此時插入數據時候,並不進行驗證操作了。
1 row inserted
SQL> insert into t values (7);
1 row inserted
SQL> commit;
commit
ORA-02091: 事務處理已回退
ORA-02290: 違反檢查約束條件 (SYS.C_T_ID1) //直到進行commit的時候,才會應用約束;
那麽,如何設置回原有的屬性呢?
SQL> set constraint c_t_id1 immediate;
Constraints set
SQL> insert into t values (4); //又恢復插入立刻檢查約束的狀態了?
insert into t values (4)
ORA-02290: 違反檢查約束條件 (SYS.C_T_ID1)
結論:單獨deferrable的含義就是是否允許約束檢查延後進行。單獨設置deferrable為deferrable之後,約束檢查延後是不可以直接使用的,要配合deferred參數,如果該參數是immediate,那麽約束還是在DML的時候進行應用。如果deferred參數設置為deferred,約束就是在事務commit提交的時候應用,出現錯誤就連帶回滾rollback整個事務。
3、 deferred:是否進行延遲應用
從上面的實驗中,我們可以看出deferred屬性是配合deferrable屬性使用的。當deferrable設置為deferrable之後,可以通過set constraints進行deferred屬性的設置,來確定約束應用時點。
本部分確定deferred定義的方式和與deferrable屬性的關系。是可以在定義約束是使用initially關鍵字來確定約束的deferred屬性取值。
SQL> alter table T
2 add constraint C_T_ID1
3 check (id>5)
4 deferrable initially deferred;
Table altered