開發提了一個資料庫變更需求,新增一字段,沒有NOT NULL非空限制,但有預設值為NULL。看起來有些奇怪,因為若字段允許NULL,其預設值就是NULL,不用顯示聲明,可以建立一個無DEFAULT NULL的新增字段再檢視desc表結構,就可以證明這點。
基于這問題,引申出的NOT NULL字段問題還有不少,也是比較容易忽視的一些細節,例如楊長老最近連續發表過兩篇關于NOT NULL字段的文章确實很有啟發,
具體各位可以參考這兩篇博文,簡單總結一下,11.2.0.3的庫,
1.使用where type is null和is not null得到的記錄結果判斷值為非空。
2.使用dump(type)和nvl(type, ‘is null’)得到的記錄結果判斷值為空。
表定義中此字段為DEFAULT ” NOT NULL,事實證明(2)是正确的,之是以有(1)的結論,原因是CBO太智能了。
對于IS NOT NULL,type字段定義為NOT NULL,此SQL明顯違反了表中的限制條件,則會在執行計劃最上層增加一個NULL IS NOT NULL恒為假的條件,根本不需要真正執行這個SQL,直接傳回0條記錄。
對于IS NULL,由于查詢條件滿足限制的條件,是以Oracle會做全表掃描,并且省略了type is not null的過濾,直接傳回所有記錄,就造成了type非空的假象。
出現以上問題的核心,還是為何有為空的記錄存儲于有NOT NULL非空限制的表中。原因就是11g新特性,新增一個有預設值的NOT NULL限制的字段,預設值不會像以前一樣,插入每條記錄中,而是會存儲于一張資料字典表sys.ecol$,Oracle允許NOT NULL列預設值為NULL,是以對于11g來說,需要禁止DEFAULT為NULL的這種行為。
這種新增非空限制字段在不同版本中确實有一些細節的變化,下面做一些簡單測試。
首先,建立測試表,插入一條資料,新增列為NOT NULL且預設值是”的字段:
10.2.0.3庫,從報錯資訊看ORA-01407,不能更新NAME列為空,可以看出此時是要将表中已存在記錄的新列name做UPDATE設定為預設值的操作,由于有非空限制,是以不允許。

11.2.0.1庫,可以新增字段,表中已存記錄該值确實為空,即允許一個有NOT NULL限制的字段包含NULL值。
關于這新特性的好處,可以參考之前寫的
《alter table新增字段操作究竟有何影響?(上下篇)》
<a href="http://blog.csdn.net/bisal/article/details/45418303">http://blog.csdn.net/bisal/article/details/45418303</a>
<a href="http://blog.csdn.net/bisal/article/details/49182025">http://blog.csdn.net/bisal/article/details/49182025</a>
12.1.0.2庫,我們可以看出和10g一樣,禁止新增一個預設值為NULL的NOT NULL限制字段,但報錯資訊變了,ORA-01758: table must be empty to add mandatory (NOT NULL) column,這個錯誤号在之前的版本有定義,不是新号。
根據錯誤提示,我們删除表中資料,再新增字段,可以增加,但不能再插入一條NULL至這個非空限制字段。
我們再看下官方文檔的描述,11g中對于新增預設值字段的描述部分,明确指出NOT NULL限制包含預設值的情況下,是将預設值存儲于資料字典中。
12c中描述允許為空的字段,若有預設值,不會更新已存資料,而是會借助資料字典完成存儲,這種新特性的适用範圍更廣了。
由于我沒有12c的sys真實環境,朋友可以自己嘗試下,有機會我會自己再驗證下。
至此,12c修複了11g中這個非空限制字段允許儲存空值的bug,同時又支援11g新增預設值非空字段使用資料字典存儲的特性,并且做了擴充支援,滿足範圍更大了。
小問題隐藏了大智慧。