Oracle資料庫開發——表(資料完整性約束)

呆呆笨笨的魚發表於2014-04-18
建立表時候,都要考慮到資料完整性,所以先提一下,資料完整性約束:
主鍵約束(primary key)、唯一性約束(unique)、檢查約束(check)、外來鍵約束(foreign key)、非空約束(not null)。

主鍵約束(primary key):主鍵可以確保一個表中沒有重複的鍵值資料行。作為主鍵的列或者列的組合,其值必須唯一,且不能為NULL。
   一個表只能定義一個主鍵約束,同時Oracle Database自動為主鍵列建立一個唯一性索引,使用者可以為該索引指定儲存位置和儲存引數。
   主鍵約束可以定義在列級,也可以定義在表級。
   由多列組成的主鍵叫做複合主鍵,一個複合主鍵中的列的數量不能超過32個。
   
唯一性約束(unique):確保列或列的組合具有唯一值(若沒有非空約束,這個值是可以為空的),Oracle Database自動為主鍵列建立一個唯一性索引,使用者可以為該索引指定儲存位置和儲存引數。
   唯一性約束可以定義在列級,也可以定義在表級。

檢查約束(check):檢查約束用來限制列的取值範圍,實現對資料的自動檢查。
  一個列可以定義多個檢查約束,其表示式中必須引用相應的列,且表示式中不能包含子查詢,SYSDATE/USER等SQL函式和ROWID、ROWNUM等偽劣。
  檢查約束可以定義在列級或表級。
  
外來鍵約束(foreign key):外來鍵約束的定義使得資料庫中表與表之間建立了父子關係。外來鍵約束用來定義子表中列的取值只能是父表中參照列的值,或者為空。
  父表中被參照的列必須有唯一性約束或主鍵約束,外來鍵約束可以定義在一列或多列組合上,可以定義在列級或表級。
  外來鍵可以是自參照約束,即外來鍵可以指向同一個表。

非空約束(not null):非空約束限制列的取值不能為NULL,一個表中可以定義多個非空約束。非空約束只能定義在列級。

例1:驗證列主鍵系統自動建立唯一索引
建立一個訂單表,訂單號為主鍵:
create table tab_orders
( order_id varchar2(20) constraint O_PK primary key
,order_date date default sysdate
,qty integer
,payterms varchar2(12)
,goods_id number(6)
);

檢視系統是否為主鍵列自動建立了唯一索引?

SQL> select t.index_name,t.index_type,t.table_name,t.uniqueness from user_indexes t where index_name = 'O_PK';
 
INDEX_NAME                     INDEX_TYPE                  TABLE_NAME                     UNIQUENESS
------------------------------ --------------------------- ------------------------------ ----------
O_PK                           NORMAL                      TAB_ORDERS                     UNIQUE
 
SQL> select x.constraint_name,x.constraint_type,x.index_name from user_constraints x where x.table_name = 'TAB_ORDERS';
 
CONSTRAINT_NAME                CONSTRAINT_TYPE INDEX_NAME
------------------------------ --------------- ------------------------------
O_PK                           P               O_PK 

結果顯示系統自動建立了唯一索引。

例2: 表級主鍵——聯合主鍵
上面一個是列級約束,那麼表級約束是怎麼建立的?
其實表級約束通常是針對多個列建立的約束,因此,表級約束的定義獨立於列的定義。以( ,)形式
create table tab_bookauthors
(author_id varchar2(15)
,book_id number(6)
,author_ord number
,constraint BA_PK primary key (author_id,author_ord)
);

SQL> select t.index_name,t.index_type,t.table_name,t.uniqueness from user_indexes t where index_name = 'BA_PK';
 
INDEX_NAME                     INDEX_TYPE                  TABLE_NAME                     UNIQUENESS
------------------------------ --------------------------- ------------------------------ ----------
BA_PK                          NORMAL                      TAB_BOOKAUTHORS                UNIQUE
 
SQL> select x.constraint_name,x.constraint_type,x.index_name from user_constraints x where x.table_name = 'TAB_BOOKAUTHORS';
 
CONSTRAINT_NAME                CONSTRAINT_TYPE INDEX_NAME
------------------------------ --------------- ------------------------------
BA_PK                          P               BA_PK
 
SQL> select * from user_ind_columns z where z.INDEX_NAME = 'BA_PK';
 
INDEX_NAME                     TABLE_NAME                     COLUMN_NAME     COLUMN_POSITION COLUMN_LENGTH CHAR_LENGTH DESCEND
------------------------------ ------------------------------ --------------- --------------- ------------- ----------- -------
BA_PK                          TAB_BOOKAUTHORS                AUTHOR_ID                     1            15          15 ASC
BA_PK                          TAB_BOOKAUTHORS                AUTHOR_ORD                    2            22           0 ASC


中間休息了兩天,下面繼續...

約束的定義
約束的定義,約束可以在建立表的時候定義,也可以在表建立完整之後,單獨新增。
create table中定義約束的語法:
定義列級約束:
create table [schema].table_name
(
column1 datatype [constraint constraint_name]
,column2 datatype [constraint constraint_name]
,...
);

帶有定義表級約束的:
create table [schema].table_name
(
column1 datatype [constraint constraint_name]
,column2 datatype [constraint constraint_name]
,...
,[constraint constraint_name] constraint_type ([column1,column2,...]|[condition])
);

先建立表,然後新增約束
create table [schema].table_name(...);

alter table [schema].table_name add constraint constraint_name constraint_type (column1,column2)[condition];

個人感覺上先建立表,然後去新增更好一些,感覺比較清晰吧= =#。
習慣上,NOT NULL 、unique、 check 約束直接在建立表的時候定義,然後單獨去定義主外來鍵約束。
還是看看oracle是怎麼儲存的吧
例3:
create table demo1
(col1 varchar2(4) constraint DO1_PK primary key
,col2 varchar2(10) unique
,col3 number not null
)
;
create table demo2
(col1 varchar2(4)
,col2 varchar2(10) 
,col3 number not null
);
alter table demo2 add constraint DO2_PK primary key(col1);
alter table demo2 add constraint DO2_UI unique(col2); 

用PL/SQL檢視建表語句
-- Create table
create table DEMO1
(
  COL1 VARCHAR2(4) not null,
  COL2 VARCHAR2(10),
  COL3 NUMBER not null
)
tablespace USERS
  pctfree 10
  initrans 1
  maxtrans 255;
-- Create/Recreate primary, unique and foreign key constraints 
alter table DEMO1
  add constraint DO1_PK primary key (COL1)
  using index 
  tablespace USERS
  pctfree 10
  initrans 2
  maxtrans 255;
alter table DEMO1
  add unique (COL2)
  using index 
  tablespace USERS
  pctfree 10
  initrans 2
  maxtrans 255;
================================================================== 
-- Create table
create table DEMO2
(
  COL1 VARCHAR2(4) not null,
  COL2 VARCHAR2(10),
  COL3 NUMBER not null
)
tablespace USERS
  pctfree 10
  initrans 1
  maxtrans 255;
-- Create/Recreate primary, unique and foreign key constraints 
alter table DEMO2
  add constraint DO2_PK primary key (COL1)
  using index 
  tablespace USERS
  pctfree 10
  initrans 2
  maxtrans 255;
alter table DEMO2
  add constraint DO2_UI unique (COL2)
  using index 
  tablespace USERS
  pctfree 10
  initrans 2
  maxtrans 255;
  
這裡可以看到表demo1在PL/SQL裡面儲存的語句被拆分開了
在檢視下使用者檢視
SQL> select x.table_name,x.constraint_name,x.constraint_type,x.index_name from user_constraints x where x.table_name in ('DEMO1','DEMO2');
 
TABLE_NAME                     CONSTRAINT_NAME                CONSTRAINT_TYPE INDEX_NAME
------------------------------ ------------------------------ --------------- ------------------------------
DEMO1                          SYS_C003893                    C               
DEMO1                          DO1_PK                         P               DO1_PK
DEMO1                          SYS_C003895                    U               SYS_C003895
DEMO2                          SYS_C003891                    C               
DEMO2                          DO2_PK                         P               DO2_PK
DEMO2                          DO2_UI                         U               DO2_UI

這裡第二列SYS開頭的是系統定義唯一標示名,因為使用者沒有定義約束名。最後一列SYS開頭是索引名唯一標示名。

看到這裡,我的感覺是雖然建立方式不同,但是oracle儲存方式是相同的。
通常先create table,再alter add約束,使用者會給約束起名字(constraint),需要系統建立唯一索引的約束,索引名與約束名相同。
直接建立的約束,有時候為了建表語句書寫的簡便(或者使用者忽略忘記了),新增約束名,系統會預設分給約束一個唯一標示名,需要系統建立唯一索引的約束,索引名與約束名相同。
以上是個人觀點,有不對的地方請指正。


新增約束
例4:
create table employees
(emp_id char(10)
,ename varchar2(20)
,job_id number(6)
,job_lv integer
,pub_id char(4)
,hiredate date
);
create table jobs
(jobid number(6) primary key
,jobname varchar2(20) not null
,minlvl integer
,maxlvl integer
);
--為employee的emp_id新增一個主鍵約束
alter table employees add constraint E_PK primary key(emp_id);

--為employee的job_id新增外來鍵,參照表為jobs的job_id
alter table employees add constraint E_FK foreign key(job_id) references jobs(jobid) on delete cascade;

--為employee的ename新增非空約束
alter table employees modify ename constraint E_NN not null;

注意:當為表新增非空約束時,必須使用modify子句,而不是add子句。


刪除約束
--刪除employees的主鍵約束
alter table employees drop constraint E_PK;  --指定約束名刪除約束

--也可以用
alter table employees drop primary key;    --指定約束內容刪除約束

注意,刪除主鍵約束、唯一約束的同時系統為自動刪除對應的唯一性索引。如果想保留唯一性索引,可以使用keep index子句。
   alter table employees drop constraint E_PK keep index;
   如果主鍵被其他外來鍵參照,可以使用cascade關鍵字指出刪除主鍵約束的同時把參照該主鍵的外來鍵一起刪除。
alter table employees drop constraint E_PK cascade;

下面來驗證
例5: 
接著例4,先檢視EMPLOYEES,JOBS的約束檢視
SQL> select x.table_name,x.constraint_name,x.constraint_type,x.index_name from user_constraints x where x.table_name in ('EMPLOYEES','JOBS');
 
TABLE_NAME                     CONSTRAINT_NAME                CONSTRAINT_TYPE INDEX_NAME
------------------------------ ------------------------------ --------------- ------------------------------
EMPLOYEES                      E_PK                           P               E_PK
EMPLOYEES                      E_FK                           R               
EMPLOYEES                      E_NN                           C               
JOBS                           SYS_C003897                    C               
JOBS                           SYS_C003898                    P               SYS_C003898


SQL> select z.table_name,z.index_type,z.table_name,z.uniqueness from user_indexes z where z.table_name in ('EMPLOYEES','JOBS');
 
TABLE_NAME                     INDEX_TYPE                  TABLE_NAME                     UNIQUENESS
------------------------------ --------------------------- ------------------------------ ----------
JOBS                           NORMAL                      JOBS                           UNIQUE
EMPLOYEES                      NORMAL                      EMPLOYEES                      UNIQUE


然後刪除EMPLOYEES的主鍵
alter table employees drop constraint E_PK;

SQL> select x.table_name,x.constraint_name,x.constraint_type,x.index_name from user_constraints x where x.table_name in ('EMPLOYEES','JOBS');
 
TABLE_NAME                     CONSTRAINT_NAME                CONSTRAINT_TYPE INDEX_NAME
------------------------------ ------------------------------ --------------- ------------------------------
EMPLOYEES                      E_FK                           R               
EMPLOYEES                      E_NN                           C               
JOBS                           SYS_C003897                    C               
JOBS                           SYS_C003898                    P               SYS_C003898


SQL> select z.table_name,z.index_type,z.table_name,z.uniqueness from user_indexes z where z.table_name in ('EMPLOYEES','JOBS');
 
TABLE_NAME                     INDEX_TYPE                  TABLE_NAME                     UNIQUENESS
------------------------------ --------------------------- ------------------------------ ----------
JOBS                           NORMAL                      JOBS                           UNIQUE

結果可以看到constraint E_PK 對應的唯一索引被刪除了。


下面刪除jobs的主鍵jobid,表employees的外來鍵參照該列
SQL> alter table jobs drop primary key cascade;
 
Table altered
 
SQL> select x.table_name,x.constraint_name,x.constraint_type,x.index_name from user_constraints x where x.table_name in ('EMPLOYEES','JOBS');
 
TABLE_NAME                     CONSTRAINT_NAME                CONSTRAINT_TYPE INDEX_NAME
------------------------------ ------------------------------ --------------- ------------------------------
EMPLOYEES                      E_NN                           C               
JOBS                           SYS_C003897                    C               
 
SQL> select z.table_name,z.index_type,z.table_name,z.uniqueness from user_indexes z where z.table_name in ('EMPLOYEES','JOBS');
 
TABLE_NAME                     INDEX_TYPE                  TABLE_NAME                     UNIQUENESS
------------------------------ --------------------------- ------------------------------ ----------

結果驗證了,如果主鍵被其他外來鍵參照,使用cascade關鍵字指出刪除主鍵約束的同時把參照該主鍵的外來鍵一起刪除。

約束的狀態和延遲檢查

(1)約束的狀態
Oracle資料庫表中的約束出於啟用狀態,即約束對錶的插入或更新操作進行檢驗,不符合約束的操作被回退。但是,實際工作中,我們有時候
需要將約束的狀態置為禁用,這樣可以提高效率。
使用 alter table ... disable ...語句

語法: alter table [schema.]table_name
                modify constraint constraint_name {enable|disable};
                
需要注意的幾點:
1.禁用主鍵使用 alter table ... disable ... cascade;後面的cascade是為了防止主鍵被其他表引用為外來鍵,而導致該主鍵約束無法禁用。
2.禁用primary key 和unique約束時,系統會將它們對應的唯一性索引刪除,並且,在重新啟用時,系統重新建立相應的唯一性索引。
 如果希望保留唯一性索引,可以在禁用時候,使用keep index子句。

(2)約束的檢查狀態
約束的啟用和禁用狀態只對設定狀態後的資料操作起作用,要想對錶中已存在的資料也進行約束檢查,則必須結合"檢查狀態"——validate(檢查)
和novaildate(非檢查)。

語法:alter table [schema.]table_name
                modify constraint constraint_name validate|novalidate;

結合,啟用 和 禁用:
組合一下
enable validate :啟用檢查狀態,Oracle資料庫預設的檢查狀態。對錶所有都進行約束條件檢查。
enable novalidate : 啟用非檢查狀態,只對更新或新插入的資料進行約束檢查,不對已存在資料進行約束檢查。
disable validate :禁用檢查狀態,在該狀態下Oralce資料庫不允許使用者進行更新或者插入操作。(因為約束已禁用卻又要求檢查,無法檢查)
disable novalidate : 禁用非檢查狀態,不對資料進行約束檢查。

(3)約束的延遲檢查

立即約束檢查 initially immediate: 預設為該選項,執行外一個DML語句後立即檢驗

延遲約束檢查 initially deferred: 當 事務提交或呼叫set constraint immediate語句時才檢驗。


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

相關文章