Oracle約束(Constraint)詳解

轉載:https://www.cnblogs.com/chengxiao/p/6032183.html
概述
  約束是數據庫用來確保數據滿足業務規則的手段,不過在真正的企業開發中,除了主鍵約束這類具有強需求的約束,像外鍵約束,檢查約束更多時候僅僅出現在數據庫設計階段,真實環境卻很少應用,更多是放到程序邏輯中去進行處理。這也比較容易理解,約束會一定程度上較低數據庫性能,有些規則直接在程序邏輯中處理就可以了,同時,也有可能在面對業務變更或是系統擴展時,數據庫約束會使得處理不夠方便。不過在我看來,數據庫約束是保證數據準確性的最後一道防線,對於設計合理的系統,處於性能考慮數據庫約束自然可有可無;不過若是面對關聯關係較爲複雜的系統,且對系統而言,數據的準確性完整性要高於性能要求,那麼這些約束還是有必要的(否則,就會出現各種相對業務規則來說莫名其妙的髒數據,本人可是深有體會的。。)。總之,對於約束的選擇無所謂合不合理,需要根據業務系統對於準確性和性能要求的側重度來決定。

數據庫約束有五種:
主鍵約束(PRIMARY KEY)
唯一性約束(UNIQUE)
非空約束(NOT NULL)
外鍵約束(FOREIGN KEY)
檢查約束(CHECK)
下面我們就分別來看下這五類約束:
數據庫約束
主鍵約束(PRIMARY KEY)

 主鍵是定位表中單個行的方式,可唯一確定表中的某一行,關係型數據庫要求所有表都應該有主鍵,不過Oracle沒有遵循此範例要求,Oracle中的表可以沒有主鍵(這種情況不多見)。關於主鍵有幾個需要注意的點:

鍵列必須必須具有唯一性,且不能爲空,其實主鍵約束 相當於 UNIQUE+NOT NULL
一個表只允許有一個主鍵
主鍵所在列必須具有索引(主鍵的唯一約束通過索引來實現),如果不存在,將會在索引添加的時候自動創建
添加主鍵(約束的添加可在建表時創建,也可如下所示在建表後添加,一般推薦建表後添加,靈活度更高一些,建表時添加某些約束會有限制)
SQL> alter table emp add constraint emp_id_pk primary key(id);

唯一性約束(UNIQUE)

 唯一性約束可作用在單列或多列上,對於這些列或列組合,唯一性約束保證每一行的唯一性。
 UNIQUE需要注意:
 對於UNIQUE約束來講,索引是必須的。如果不存在,就自動創建一個(UNIQUE的唯一性本質上是通過索引來保證的)
 UNIQUE允許null值,UNIQUE約束的列可存在多個null。這是因爲,Unique唯一性通過btree索引來實現,而btree索引中不包含null。當然,這也造成了在where語句中用null值進行過濾會造成全表掃描。
 添加唯一約束

SQL> alter table emp add constraint emp_code_uq unique(code);
非空約束(NOT NULL)

非空約束作用的列也叫強制列。顧名思義,強制鍵列中必須有值,當然建表時候若使用default關鍵字指定了默認值,則可不輸入。

  添加非空約束,語法較特別

SQL> alter table emp modify ename not null;
外鍵約束(FOREIGN KEY)

外鍵約束定義在具有父子關係的子表中,外鍵約束使得子表中的列對應父表的主鍵列,用以維護數據庫的完整性。不過出於性能和後期的業務系統的擴展的考慮,很多時候,外鍵約束僅出現在數據庫的設計中,實際會放在業務程序中進行處理。外鍵約束注意以下幾點:

  外鍵約束的子表中的列和對應父表中的列數據類型必須相同,列名可以不同
  對應的父表列必須存在主鍵約束(PRIMARY KEY)或唯一約束(UNIQUE)
  外鍵約束列允許NULL值,對應的行就成了孤行了
  其實很多時候不使用外鍵,很多人認爲會讓刪除操作比較麻煩,比如要刪除父表中的某條數據,但某個子表中又有對該條數據的引用,這時就會導致刪除失敗。我們有兩種方式來優化這種場景:

  第一種方式簡單粗暴,刪除的時候,級聯刪除掉子表中的所有匹配行,在創建外鍵時,通過 on delete cascade 子句指定該外鍵列可級聯刪除:

SQL> alter table emp add constraint emp_deptno_fk foreign key(deptno) references dept (deptno) on delete cascade;
  第二種方式,刪除父表中的對應行,會將對應子表中的所有匹配行的外鍵約束列置爲NULL,通過 on delete set null 子句實施:

SQL> alter table emp add constraint emp_deptno_fk foreign key(deptno) references dept(deptno) on delete set null;
實際上,外鍵約束列和對應的父表列可以在同一張表中,常見的就是表的業務邏輯含義是一棵樹,最簡單的例子如下(id爲主鍵id,fid爲父id,fid存儲對id的引用),這種結構的表根據業務要求可通過Oracle的遞歸查詢來獲取這種層級關係

檢查約束(CHECK)

檢查約束可用來實施一些簡單的規則,比如列值必須在某個範圍內。檢查的規則必須是一個結果爲true或false 的表達式,比如:

SQL> alter table emp add constraint emp_sex_ck check(sex in('男','女'));
約束狀態
  很多時候由於業務需要,比如我們有大量的歷史數據,需要和現有數據合併,當前表存在數據庫約束(如非空約束),而這些歷史數據又包含違背非空約束的數據行,爲了避免導入時由於違反約束而導入失敗,我們通過調整約束狀態來達到目的。

數據庫約束有兩類狀態

  啓用/禁用(enable/disable):是否對新變更的數據啓用約束驗證

  驗證/非驗證 (validate/novalidate) :是否對錶中已客觀存在的數據進行約束驗證

這兩類四種狀態從語法角度講可以隨意組合,默認是 enable validate

下面我們來看着四類組合會分別出現什麼樣的效果:

enable validate : 默認的約束組合狀態,無法添加違反約束的數據行,數據表中也不能存在違反約束的數據行;

enable novalidate : 無法添加違反約束的數據行,但對已存在的違反約束的數據行不做驗證;

disable validate : 可以添加違反約束的數據行,但對已存在的違反約束的數據行會做約束驗證(從描述中可以看出來,這本來就是一種相互矛盾的約束組合,只不過是語法上支持這種組合罷了,造成的結果就是會導致DML失敗)

disable novalidate : 可以添加違法約束的數據行,對已存在的違反約束的數據行也不做驗證。

拿上面的例子來說,我們需要上傳大量違反非空約束的歷史數據(從業務角度講這些數據不會造成系統功能異常),可以臨時將約束狀態轉爲 disable novalidate,以保證這些不合要求的數據導入表中

SQL> alter table emp modify constraint emp_ename_nn disable novalidate;
在數據導入完成之後,我們再將約束狀態轉爲enable novalidate 以確保之後添加的數據不會再違反約束

SQL> alter table emp modify constraint emp_ename_nn enable novalidate;

總結
  本文介紹了數據庫中的五類約束,也提到了數據庫約束的四種狀態組合,當你由於業務需求或是系統擴展,在一個約束嚴苛的系統中由於約束限制頻繁操作失敗的時候,不同組合的約束狀態或許能給你另一種處理方案。謝謝支持。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章