[.NET][Database] 資料庫中的 NULL 值

在資料表結構 (table schema) 中,每個欄位除了基本的欄位名稱、型態、大小等資料外,有一個有趣的欄位很特別,就是是否允許 NULL,NULL 這個東西對程式設計師來說是又愛又恨,有時會被它搞得嫑嫑的,但是它有時卻又是一個有必要的存在。

本文中,以 NULL 代表資料庫中的 NULL 值,以 null 代表程式中的 null 指標。

NULL 在資料庫裡面代表的是未知的屬性值 (unknown property value),在一個資料表的結構裡面,雖然大多數的屬性可能是已經有的定義 (源自系統設計的資料結構定義),但有些情況,當資料在某些狀態之下,某些欄位的值用什麼樣的數值替代都沒有意義,這時候就會使用 NULL 來替代。

例如一個訂單資料表,裡面有 OrderId, OrderDate, ShipDate, Status 等欄位,其中 OrderId, OrderDate, Status 在訂單建立時就會有值 (訂單編號、訂單日期、狀態 = OrderStatus.Pending),但 ShipDate 只會在訂單出貨時才會有記錄,在訂單建立時硬給 ShipDate 的值是沒有意義的 (例如給 DateTime.MinValue),若代入值會給程式帶來困擾時,就會使用 NULL 值來替代,表示這個欄位沒有任何的值。

NULL 是空值的說法只在某些情境成立,因此請不要再說 NULL 是空值了。

由於 NULL 值是一種未知的資料,在資料庫裡面的定義基本上對 NULL 的運算都會回傳 NULL 值,各種類型的資料庫對 NULL 的判斷也不盡相同,像 SQL Server 使用的是 ISNULL()、MySQL 則是用 IFNULL()、Oracle 是用 NVL/NVL2 等方式,決定當發現值為 NULL 時就代換成某個預設值,當然也可以直接回傳 NULL。

不過,資料庫的 NULL 值和程式裡面的 null 完全不同,資料庫的 NULL 是一個值,以 .NET 來說,NULL 值會用 DBNull 類別來替代,程式設計師可以用 DBNull.Value 來取得;程式的 null 則是指沒有指向任何物件的指標 (也就是 null 指標),對宣告成 null 的變數進行存取會擲回 NullReferenceException,因此對待 NULL 和 null 的方式必須完全不同,在 IDbDataReader 裡面也有個IsDBNull(),能用來判斷目前資料列的特定屬性是否為 NULL。在 .NET Framework 中的文件也已經有提到了:

請勿將物件導向程式語言中 Null 參照 (即 Visual Basic 中的 Nothing) 的概念與 DBNull 物件混為一談。在物件導向程式語言中,Null 參照 (即 Visual Basic 中的 Nothing) 表示物件的參考不存在。DBNull 則表示未初始化的變數或不存在的資料庫資料行。

對付資料庫的 NULL 值對程式設計師而言是種挑戰,資料庫裡面存放的資料是實值型態 (Value Type),但是 .NET 的實值型態是不能設成 null 的,因此有不少程式設計值在處理 NULL 的時候被搞得嫑嫑的。

NULL 和 null 本來就是不一樣的東西,NULL == null 是不可能會回傳 true 的,而 null == NULL 也永遠不可能回傳 true。

然後一堆奇怪的作法就衍生出來了,像是避免 NULL 值的出現 (例如這篇文),或是使用一個預設值去替代。

string 的問題不大,因為 string 本身是可 null 的型別。

基於 NULL 和 null 是不同的東西,.NET 3.5 加入了一個新的物件 Nullable,並實作了一個泛型類別 Nullable<T>,擴充了所有 .NET 的實值型資料型態,例如:

Nullable<int> -> int?
Nullable<bool> -> bool?
Nullable<double> -> double?
Nullable<DateTime> -> DateTime?

Nullable 的出現為程式設計師減少了相當程度的判斷,程式設計師再也不需要為了 NULL 值去寫其他的程式判斷,只要在裝資料時先判斷 DBNull,再使用 Nullable 物件存放值,當 Nullable 裡面的值是 NULL 時,Nullable.HasValue 會回傳 false,當然也可以用 == 來判斷。

NULL 的存在是基於資料結構的設計,資料結構的設計又是源自於需求規格或系統分析規格,因此不可以隨便用特定的預設值去替代,這會破壞原本系統設計的結構,當遇到 NULL 值的時候,也應該要用正確的觀念去看待它,而不是隨便的去解讀它的意思,畢竟 NULL 代表的就是 ... 未知。

References:

1. Fundamentals of Database Systems, 6/e
2. 維基百科的 NULL 條目
3. 使用可為 null 的類型