oracle約束學習經典案例

dawn009發表於2014-03-07
今天處理了一個由於約束插入資料失敗的問題,處理時感到有些吃力,三天不練手生啊。在這裡回憶一下。
Oracle資料庫Constraint約束的四對屬性:Deferrable/not deferrable, Deferred/immediate, enable/disable, validate/novalidate。


準備一下環境:
SQL> create table empx as select * from emp;

SQL> create table deptx as select * from dept;

SQL> alter table empx add constraint pk_empx primary key(empno);

SQL> alter table deptx add constraint pk_deptx primary key(deptno);

SQL> alter table empx add constraint fk_deptx foreign key (deptno) references deptx(deptno);

SQL> select * from deptx;

[plain] view plaincopy
  1.    DEPTNO DNAME                   LOC  
  2. --------- ------------------------------------------ ---------------------------------------  
  3. 10 ACCOUNTING                     NEW YORK  
  4. 20 RESEARCH                   DALLAS  
  5. 30 SALES                      CHICAGO  
  6. 40 OPERATIONS                     BOSTON  

SQL> select * from empx;

[plain] view plaincopy
  1.      EMPNO ENAME              JOB                    MGR HIREDATE       SAL   COMM     DEPTNO  
  2. ---------- ------------------------------ --------------------------- ---------- --------------- ---------- ---------- ----------  
  3.       7369 SMITH              CLERK                 7902 17-DEC-80      800            20  
  4.       7499 ALLEN              SALESMAN              7698 20-FEB-81         1600    300         30  
  5.       7521 WARD               SALESMAN              7698 22-FEB-81         1250    500         30  
  6.       7566 JONES              MANAGER               7839 02-APR-81         2975            20  
  7.       7654 MARTIN             SALESMAN              7698 28-SEP-81         1250   1400         30  
  8.       7698 BLAKE              MANAGER               7839 01-MAY-81         2850            30  
  9.       7782 CLARK              MANAGER               7839 09-JUN-81         2450            10  
  10.       7788 SCOTT              ANALYST               7566 19-APR-87         3000            20  
  11.       7839 KING               PRESIDENT              17-NOV-81         5000            10  
  12.       7844 TURNER             SALESMAN              7698 08-SEP-81         1500      0         30  
  13.       7876 ADAMS              CLERK                 7788 23-MAY-87         1100            20  
  14.       7900 JAMES              CLERK                 7698 03-DEC-81      950            30  
  15.       7902 FORD               ANALYST               7566 03-DEC-81         3000            20  
  16.       7934 MILLER             CLERK                 7782 23-JAN-82         1300            10  
  17.   
  18. 14 rows selected.  

SQL> select constraint_name, SEARCH_CONDITION cond, STATUS, DEFERRABLE, DEFERRED, VALIDATED from user_constraints where table_name='EMPX';

[plain] view plaincopy
  1. CONSTRAINT_NAME      COND         STATUS           DEFERRABLE       DEFERRED         VALIDATED  
  2. ------------------------ ---------------- ------------------------ ---------------- ---------------- ------------  
  3. PK_EMPX                   ENABLED          NOT DEFERRABLE   IMMEDIATE        VALIDATED  
  4. FK_DEPTX                  ENABLED          NOT DEFERRABLE   IMMEDIATE        VALIDATED  

SQL> insert into empx (empno,ename,job,mgr,hiredate,sal,deptno) values (8000,'TOM','SALESMAN',7839,sysdate,2000,50);
insert into empx (empno,ename,job,mgr,hiredate,sal,deptno) values (8000,'TOM','SALESMAN',7839,sysdate,2000,50)
*
ERROR at line 1:
ORA-02291: integrity constraint (SCOTT.FK_DEPTX) violated - parent key not found

這裡透過emp和dept表建立了兩張表empx和deptx,並在empx表上建立了外來鍵約束fk_deptx。

1.Deferrable,not deferrable(default value)

1)這對屬性是定義是否可以延時驗證,deferrable是指作驗證時機,如果在commit的時check為deferrable,否則為immediate .只有在設定Deferrable才可以設定另一屬性Deferred,immediate.

2)這對屬性是在建立的constraint的時候定義的,不能被修改.

notice:如果建立了Deferrable的uk或pk,只會建立相應的nonuniquce index,而不會建立uniquce index

SQL> alter table empx drop constraint fk_deptx;

SQL> alter table empx add constraint fk_deptx foreign key (deptno) references deptx(deptno) deferrable;

SQL> select constraint_name, SEARCH_CONDITION cond, STATUS, DEFERRABLE, DEFERRED, VALIDATED from user_constraints where table_name='EMPX';

[plain] view plaincopy
  1. CONSTRAINT_NAME      COND         STATUS           DEFERRABLE       DEFERRED         VALIDATED  
  2. ------------------------ ---------------- ------------------------ ---------------- ---------------- ------------  
  3. PK_EMPX                   ENABLED          NOT DEFERRABLE   IMMEDIATE        VALIDATED  
  4. FK_DEPTX                  ENABLED          DEFERRABLE       IMMEDIATE        VALIDATED  

SQL> insert into empx (empno,ename,job,mgr,hiredate,sal,deptno) values (8000,'TOM','SALESMAN',7839,sysdate,2000,50);
insert into empx (empno,ename,job,mgr,hiredate,sal,deptno) values (8000,'TOM','SALESMAN',7839,sysdate,2000,50)
*
ERROR at line 1:
ORA-02291: integrity constraint (SCOTT.FK_DEPTX) violated - parent key not found

在會話級設定:
SQL> set constraint FK_DEPTX deferred;

SQL>  select constraint_name, SEARCH_CONDITION cond, STATUS, DEFERRABLE, DEFERRED, VALIDATED from user_constraints where table_name='EMPX';

[plain] view plaincopy
  1. CONSTRAINT_NAME  COND         STATUS           DEFERRABLE       DEFERRED            VALIDATED  
  2. ---------------- ---------------- ------------------------ ---------------- --------------------------- ----------------  
  3. PK_EMPX               ENABLED          NOT DEFERRABLE   IMMEDIATE           VALIDATED  
  4. FK_DEPTX              ENABLED          DEFERRABLE       IMMEDIATE           VALIDATED  

SQL> insert into empx (empno,ename,job,mgr,hiredate,sal,deptno) values (8000,'TOM','SALESMAN',7839,sysdate,2000,50);

1 row created.

SQL> commit;
commit
*
ERROR at line 1:
ORA-02091: transaction rolled back
ORA-02291: integrity constraint (SCOTT.FK_DEPTX) violated - parent key not found


單獨deferrable的含義就是是否允許約束檢查延後進行。單獨設定deferrable為deferrable之後,約束檢查延後是不可以直接使用的,要配合deferred引數,如果該引數是immediate,那麼約束還是在DML的時候進行應用。如果deferred引數設定為deferred,約束就是在事務commit提交的時候應用,出現錯誤就連帶回滾rollback整個事務。

2.Deferred,immediate(default value)

deferred屬性是配合deferrable屬性使用的。當deferrable設定為deferrable之後,可以透過set constraints在會話級進行deferred屬性的設定,來確定約束應用時點。
定義約束是使用initially關鍵字來確定約束的deferred屬性取值。

SQL> alter table empx drop constraint fk_deptx;

SQL> alter table empx add constraint fk_deptx foreign key (deptno) references deptx(deptno) deferrable initially deferred;

SQL> select constraint_name, SEARCH_CONDITION cond, STATUS, DEFERRABLE, DEFERRED, VALIDATED from user_constraints where table_name='EMPX';

[plain] view plaincopy
  1. CONSTRAINT_NAME  COND         STATUS           DEFERRABLE       DEFERRED            VALIDATED  
  2. ---------------- ---------------- ------------------------ ---------------- --------------------------- ----------------  
  3. PK_EMPX               ENABLED          NOT DEFERRABLE   IMMEDIATE           VALIDATED  
  4. FK_DEPTX              ENABLED          DEFERRABLE       DEFERRED            VALIDATED  

SQL>  insert into empx (empno,ename,job,mgr,hiredate,sal,deptno) values (8000,'TOM','SALESMAN',7839,sysdate,2000,50);

1 row created.

SQL> commit;
commit
*
ERROR at line 1:
ORA-02091: transaction rolled back
ORA-02291: integrity constraint (SCOTT.FK_DEPTX) violated - parent key not found

可以透過建立contraint時指定Deferred值,也可以透過會話級別的語句SET CONSTRAINT(s)來設定(如上面的set constraint FK_DEPTX deferred)。

當屬性DEFERRABLE和DEFERRED的值分別為DEFERRABLE和DEFERRED時,在事務提交時才驗證,如果驗證沒透過,則報錯,事務回滾。

3. novalidate, validate(default value)

定義是否對錶中已經存在的資料作檢查。

刪除約束並插入一條髒資料:
SQL> alter table empx drop constraint fk_deptx;

Table altered.

SQL>  insert into empx (empno,ename,job,mgr,hiredate,sal,deptno) values (8000,'TOM','SALESMAN',7839,sysdate,2000,50);

1 row created.

SQL> commit;

Commit complete.

SQL> alter table empx add constraint fk_deptx foreign key (deptno) references deptx(deptno);
alter table empx add constraint fk_deptx foreign key (deptno) references deptx(deptno) deferrable initially deferred
                                *
ERROR at line 1:
ORA-02298: cannot validate (SCOTT.FK_DEPTX) - parent keys not found

SQL> alter table empx add constraint fk_deptx foreign key (deptno) references deptx(deptno) deferrable initially deferred;
alter table empx add constraint fk_deptx foreign key (deptno) references deptx(deptno) deferrable initially deferred
                                *
ERROR at line 1:
ORA-02298: cannot validate (SCOTT.FK_DEPTX) - parent keys not found

語句中不帶validate屬性引數,預設建立的是validate的約束。由於有髒資料的存在,不管是建立延時還是非延時的約束都不成功。

SQL> alter table empx add constraint fk_deptx foreign key (deptno) references deptx(deptno) novalidate;

Table altered.

建立novalidate的約束成功。

SQL> select constraint_name, SEARCH_CONDITION cond, STATUS, DEFERRABLE, DEFERRED, VALIDATED from user_constraints where table_name='EMPX';

[plain] view plaincopy
  1. CONSTRAINT_NAME  COND         STATUS           DEFERRABLE       DEFERRED         VALIDATED  
  2. ---------------- ---------------- ------------------------ ---------------- ---------------- ----------------  
  3. PK_EMPX               ENABLED          NOT DEFERRABLE   IMMEDIATE        VALIDATED  
  4. FK_DEPTX              ENABLED          NOT DEFERRABLE   IMMEDIATE        NOT VALIDATED  

SQL> select * from empx;

[plain] view plaincopy
  1. EMPNO ENAME           JOB                    MGR HIREDATE       SAL   COMM     DEPTNO  
  2. ----- ------------------------------ --------------------------- ---------- --------------- ---------- ---------- ----------  
  3.  7369 SMITH           CLERK                 7902 17-DEC-80      800            20  
  4.  7499 ALLEN           SALESMAN              7698 20-FEB-81         1600    300         30  
  5.  7521 WARD            SALESMAN              7698 22-FEB-81         1250    500         30  
  6.  7566 JONES           MANAGER               7839 02-APR-81         2975            20  
  7.  7654 MARTIN              SALESMAN              7698 28-SEP-81         1250   1400         30  
  8.  7698 BLAKE           MANAGER               7839 01-MAY-81         2850            30  
  9.  7782 CLARK           MANAGER               7839 09-JUN-81         2450            10  
  10.  7788 SCOTT           ANALYST               7566 19-APR-87         3000            20  
  11.  7839 KING            PRESIDENT              17-NOV-81         5000            10  
  12.  7844 TURNER              SALESMAN              7698 08-SEP-81         1500      0         30  
  13.  7876 ADAMS           CLERK                 7788 23-MAY-87         1100            20  
  14.  7900 JAMES           CLERK                 7698 03-DEC-81      950            30  
  15.  7902 FORD            ANALYST               7566 03-DEC-81         3000            20  
  16.  7934 MILLER              CLERK                 7782 23-JAN-82         1300            10  
  17.  8000 TOM                 SALESMAN              7839 15-JUL-12         2000            50  

有一條髒資料存在,看看能不能再增加一條:

SQL> insert into empx (empno,ename,job,mgr,hiredate,sal,deptno) values (8001,'TOM','SALESMAN',7839,sysdate,2000,50);
insert into empx (empno,ename,job,mgr,hiredate,sal,deptno) values (8001,'TOM','SALESMAN',7839,sysdate,2000,50)
*
ERROR at line 1:
ORA-02291: integrity constraint (SCOTT.FK_DEPTX) violated - parent key not found


不能增加違反約束的資料,novalidate的約束只是對原來的資料庫不做驗證,但對新插入的資料還是要進行驗證的。


4. disable, enalbe(default value)

啟用和禁用constraint.如果在新建pk和uk時定義了disable,將不建立相應的索引.

SQL> alter table empx disable constraint fk_deptx;

Table altered.

SQL> select constraint_name, SEARCH_CONDITION cond, STATUS, DEFERRABLE, DEFERRED, VALIDATED from user_constraints where table_name='EMPX';

[plain] view plaincopy
  1. CONSTRAINT_NAME  COND         STATUS           DEFERRABLE       DEFERRED         VALIDATED  
  2. ---------------- ---------------- ------------------------ ---------------- ---------------- ----------------  
  3. PK_EMPX               ENABLED          NOT DEFERRABLE   IMMEDIATE        VALIDATED  
  4. FK_DEPTX              DISABLED         NOT DEFERRABLE   IMMEDIATE        NOT VALIDATED  

SQL> insert into empx (empno,ename,job,mgr,hiredate,sal,deptno) values (8001,'TOM','SALESMAN',7839,sysdate,2000,50);

1 row created.

SQL> commit;

Commit complete.

禁用了約束後,髒資料也能插入成功。

SQL> alter table empx enable constraint fk_deptx;
alter table empx enable constraint fk_deptx
                                   *
ERROR at line 1:
ORA-02298: cannot validate (SCOTT.FK_DEPTX) - parent keys not found

SQL> select constraint_name, SEARCH_CONDITION cond, STATUS, DEFERRABLE, DEFERRED, VALIDATED from user_constraints where table_name='EMPX';

[plain] view plaincopy
  1. CONSTRAINT_NAME  COND         STATUS           DEFERRABLE       DEFERRED         VALIDATED  
  2. ---------------- ---------------- ------------------------ ---------------- ---------------- ----------------  
  3. PK_EMPX               ENABLED          NOT DEFERRABLE   IMMEDIATE        VALIDATED  
  4. FK_DEPTX              DISABLED         NOT DEFERRABLE   IMMEDIATE        NOT VALIDATED  

SQL> alter table empx enable novalidate constraint fk_deptx;

Table altered.

SQL> select constraint_name, SEARCH_CONDITION cond, STATUS, DEFERRABLE, DEFERRED, VALIDATED from user_constraints where table_name='EMPX';

[plain] view plaincopy
  1. CONSTRAINT_NAME  COND         STATUS           DEFERRABLE       DEFERRED         VALIDATED  
  2. ---------------- ---------------- ------------------------ ---------------- ---------------- ----------------  
  3. PK_EMPX               ENABLED          NOT DEFERRABLE   IMMEDIATE        VALIDATED  
  4. FK_DEPTX              ENABLED          NOT DEFERRABLE   IMMEDIATE        NOT VALIDATED  

如果有髒資料,啟用約束時要同時設定novalidate。

由上面的實驗得知,預設情況下,Oracle constraint是不開啟延遲約束和原有資料保留驗證的。

下列情況下可以使用這些特性:
1)批次資料匯入、物化檢視重新整理的時候,事務量很大,而且存在資料規律前後顛倒的情況
此時,如果開啟著立即約束應用的開關,可能存在一些暫時性的約束不滿足的情況,從而影響到整個系統的執行。開啟約束延遲驗證,就可以幫助我們解決這個問題;
2)在歷史資料移植
歷史資料存在不規則的情況,很多時候難以滿足我們新系統的資料完整性要求。可以使用not validate的方式,對歷史資料不進行約束控制,而只針對新資料開啟驗證。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/29119536/viewspace-1103056/,如需轉載,請註明出處,否則將追究法律責任。

相關文章