程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 數據庫知識 >> Oracle數據庫 >> Oracle教程 >> 從NOTNULL完整性約束的介紹看Concept的強大

從NOTNULL完整性約束的介紹看Concept的強大

編輯:Oracle教程

從NOTNULL完整性約束的介紹看Concept的強大


Oracle Concept是我們平日學習的官方寶典,之所以稱為寶典,一個原因就是很多問題,歸根結底可能都可以從Concept中找到一些說明和出處,記得有一次Kamus回答網友問題的時候(具體問題忘了),援引的就是Concept某一章節的一句話。另一個原因就是其內容表述言簡意赅,一些使用上的細節用簡單的幾個單詞或幾句話就帶出來了,但往往卻包含著豐富的含義,有點於無聲處聽驚雷的感覺。

例如第五章介紹NOT NULL Integrity Constraints的時候,有一句:

You can only add a column with a NOT NULL constraint if the table does not contain any rows or if you specify a default value.

翻譯過來就是,只有當表中無數據或者包含默認值的情況下,可以為列增加一個NOT NULL約束。簡單的一句話,其實蘊含著不少信息,我們用實驗來說明。

環境准備:11.2.0.4

SQL> create table tbl_null(
2 id number primary key,
3 name varchar2(5)
4 );
Table created.

SQL> insert into tbl_null values(1, 'a');
1 row created.

SQL> insert into tbl_null values(2, '');
1 row created.

SQL> select * from tbl_null;
ID NAME
---------- -----
1 a
2

這裡創建了一張測試表TBL_NULL,其中包含一個name字段,VARCHAR2類型,允許為空,有兩條記錄,其中一條name為空。

實驗1:驗證“當表中無數據”時才可以增加NOT NULL約束。
此時表中有一條name是空的記錄,如果執行增加非空約束的操作:

SQL> alter table tbl_null modify name not null;
alter table tbl_null modify name not null
*
ERROR at line 1:
ORA-02296: cannot enable (BISAL.) - null values found

由於name字段有一條記錄是空值,因此就會提示發現null值,禁止執行。
刪除數據後執行:

SQL> truncate table tbl_null;
Table altered.

SQL> alter table tbl_null modify name not null;
Table altered.

表中無數據時可以隨意增加NOT NULL約束。

實驗2:驗證“包含默認值”的情況下,可以新增NOT NULL約束。

SQL> select * from tbl_null;
    ID NAME
---------- -----
     1 a

SQL> alter table tbl_null modify name not null;
Table altered.

當表中數據name字段都有值的情況下,可以新增NOT NULL約束。很好理解,如果字段值當前為空,又要設置NOT NULL約束,Oracle不知道如何設置這些空值。

實驗3:表中記錄有空值,還要增加NOT NULL約束。
那麼問題來了,如果表中記錄有空值,還要增加NOT NULL約束,但又不能按照實驗1先清空數據做,也不能按照實驗2都賦予一個默認值來做,怎麼辦?
此時可以使用enable novalidate選項,讓其增加NOT NULL約束時不對已存在數據進行非空檢查。

SQL> select * from tbl_null;
ID NAME
---------- -----
1 a
2

SQL> alter table tbl_null modify name not null enable novalidate;
Table altered.

SQL> insert into tbl_null values(3, '');
insert into tbl_null values(3, '')
*
ERROR at line 1:
ORA-01400: cannot insert NULL into ("BISAL"."TBL_NULL"."NAME")

SQL> select * from tbl_null;
ID NAME
---------- -----
1 a
2

可以看出,新增空值的操作報錯,提示不允許插入空值,但實際上表中存在name為空的記錄。即使用enable novalidate選項時,Oracle不會校驗歷史數據是否符合約束,只會對新增數據進行校驗。
官方對於enable novalidate的解釋:

The database checks the constraint, but it need not be true for all rows. Thus, existing rows can violate the constraint, but new or modified rows must conform to the rules.

?如果應用上對於字段NOT NULL的約束有特殊處理,可以在此時UPDATE舊數據的空值為默認值,保證表中該字段值都有值,即在表中字段有空值的情況下新增NOT NULL約束。

實驗4:使用和不使用enable novalidate有什麼區別?
進一步,我們再看看使用和不使用enable novalidate後台的區別。

1.創建測試表,模擬5000000行記錄:

SQL> declare 
  2    n number;
  3    j number;
  4  begin
  5    for i in 1 .. 5000000 loop
  6      insert into tbl_null values(i, 'A');
  7      n := mod(i, 100000);
  8      if (n = 0) then
  9        j := i/100000;
 10        dbms_output.put_line('the '||j||' insert');
 11        commit;
 12      end if;
 13    end loop;
 14    commit;
 15  end;
 16  /
PL/SQL procedure successfully completed.

SQL> select count(*) from tbl_null;
  COUNT(*)
----------
   5000000

2.分別使用和不使用enable novalidate進行測試:

SQL> alter table tbl_null modify name not null;
Table altered.
Elapse: 00:00:00.17

SQL> alter table tbl_null_0 modify name not null enable novalidate;
Table altered.
Elapsed: 00:00:00.05

粗略地看,使用enable novalidate的方法會比不使用要略快一些。

3.從10046的trace文件看出,使用和不使用enable novalidate,首先都會執行以下SQL:

LOCK TABLE "TBL_NULL" IN EXCLUSIVE MODE  NOWAIT

以NOWAIT方式對TBL_NULL表加上排他鎖。但不使用enable novalidate則會有以下和TBL_NULL相關的兩步額外操作:

select /*+ all_rows ordered */ A.rowid, :1, :2, :3 
from
 "BISAL"."TBL_NULL" A where( "NAME" is null)
SELECT /* OPT_DYN_SAMP */ /*+ ALL_ROWS IGNORE_WHERE_CLAUSE 
  NO_PARALLEL(SAMPLESUB) opt_param('parallel_execution_enabled', 'false') 
  NO_PARALLEL_INDEX(SAMPLESUB) NO_SQL_TUNE */ NVL(SUM(C1),0), NVL(SUM(C2),0) 
FROM
 (SELECT /*+ IGNORE_WHERE_CLAUSE NO_PARALLEL("A") FULL("A") 
  NO_PARALLEL_INDEX("A") */ 1 AS C1, CASE WHEN "A"."NAME" IS NULL THEN 1 ELSE 
  0 END AS C2 FROM "BISAL"."TBL_NULL" "A") SAMPLESUB

從這兩句SQL可以看出他要檢索所有name值是空的記錄,且使用的都是全表掃描。雖然我還不是很清楚這兩步操作的真正意義,但一定程度上可以說明使用enable novalidate快的一些可能原因。

總結
增加NOT NULL約束的操作,看似非常簡單,官方文檔中也是輕描淡寫地幾句話就介紹完了,但實際上我們還是可以用實驗測試每個條件,深刻體會其背後的一些原因。而且Oracle很多知識點之間是互相關聯的,eygle曾經說過“由點及面”地學習Oracle,我想這就是這個道理了。
當然,讓我體會更深的是,自己還是有太多要學習的知識,作為一個Oracle初學者,我仍在路上。。。共勉。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved