關於建表欄位是否該使用not null這個問題你怎麼看?

威哥爱编程發表於2024-09-25

大家好,我是 V 哥,在資料庫設計中,是否使用 NOT NULL 是一個非常重要的決策,直接影響資料完整性、查詢效能以及業務邏輯的複雜度。使用 NOT NULL 的關鍵在於理解業務需求和具體場景。

下面V哥透過一些場景來分析什麼時候應該使用 NOT NULL,什麼時候允許 NULL。一起聊聊經驗之談,望和兄弟們討論。

1. 必須存在值的欄位

對於某些關鍵欄位,如果業務邏輯要求它們始終具有值,那麼應該使用 NOT NULL 約束。這樣可以防止資料不完整,避免潛在的業務問題。

示例:使用者註冊場景

  • 欄位usernameemail
  • 分析:使用者在註冊時,使用者名稱和郵箱是必須的。缺少這兩個欄位會導致後續的登入或通知無法進行,因此這些欄位應該設定為 NOT NULL
CREATE TABLE users (
    id INT PRIMARY KEY,
    username VARCHAR(255) NOT NULL,
    email VARCHAR(255) NOT NULL
);

影響:

  • 資料完整性:確保任何使用者都具備使用者名稱和郵箱。
  • 效能最佳化:NOT NULL 欄位可以最佳化資料庫的索引和查詢效率。

2. 可選欄位

有些欄位是可選的,它們的缺失不會對業務邏輯造成影響。在這種情況下,允許 NULL 可以為業務提供靈活性,避免強制要求使用者提供所有資訊。

示例:使用者資訊場景

  • 欄位middle_nameprofile_picture
  • 分析:中間名和個人資料圖片通常是可選資訊。如果強制使用 NOT NULL,則需要提供預設值,這在某些情況下不太合理。比如使用者不提供個人資料圖片,欄位可以設定為 NULL
CREATE TABLE user_profiles (
    id INT PRIMARY KEY,
    middle_name VARCHAR(255),
    profile_picture VARCHAR(255)
);

影響:

  • 靈活性:允許欄位為空為使用者提供更多自由,同時保持資料結構的靈活性。
  • 業務適應性:隨著業務需求的變化,允許 NULL 的欄位可以容納更多的邊緣情況。

3. 需要標識未知狀態的欄位

在某些業務場景中,NULL 可以表示“未知”或“未提供”的狀態,而不僅僅是“空值”。這種情況下,允許 NULL 是合理的,因為它能明確區分“沒有值”和“值為空”。

示例:訂單處理場景

  • 欄位shipped_date
  • 分析:訂單的發貨日期在建立時可能未知,只有在訂單發貨後才會填入。如果使用 NULL 表示未發貨狀態,可以簡化業務邏輯,並且更符合直觀的業務需求。
CREATE TABLE orders (
    id INT PRIMARY KEY,
    order_date DATE NOT NULL,
    shipped_date DATE
);

影響:

  • 狀態表達:NULL 可以清晰地表示某個狀態未發生,如訂單尚未發貨。
  • 簡化業務邏輯:不用新增額外的布林欄位來表示是否發貨。

4. 外來鍵欄位和 NOT NULL

外來鍵的設計中,是否使用 NOT NULL 依賴於業務邏輯。強制 NOT NULL 意味著關聯關係是強制性的;允許 NULL 則表示某些記錄可能暫時沒有關聯項。

示例:部落格文章和作者

  • 欄位author_id
  • 分析:如果業務邏輯允許某些文章沒有具體作者(如系統自動生成),那麼 author_id 欄位可以允許 NULL。如果每篇文章都必須有作者,則 NOT NULL 更為合理。
CREATE TABLE posts (
    id INT PRIMARY KEY,
    title VARCHAR(255) NOT NULL,
    content TEXT NOT NULL,
    author_id INT REFERENCES users(id)  -- 可以是 NULL
);

影響:

  • 強制關聯關係:使用 NOT NULL 可以確保資料的完整性和一致性。
  • 靈活關聯:允許 NULL 的外來鍵欄位為業務提供更多靈活性,支援特殊場景。

5. 效能與儲存開銷

從效能角度來看,NOT NULL 欄位在某些情況下可以加速查詢。因為資料庫可以更有效地處理不允許 NULL 的欄位,不需要對 NULL 值進行額外的判斷。然而,如果太多欄位都設定為 NOT NULL,則可能導致業務複雜性增加。

示例:高頻查詢欄位

  • 欄位last_login
  • 分析:對於使用者的登入系統,查詢最後登入時間是常見操作。如果該欄位被設定為 NOT NULL,資料庫可以更快地檢索資料。
CREATE TABLE user_sessions (
    id INT PRIMARY KEY,
    user_id INT NOT NULL,
    last_login TIMESTAMP NOT NULL
);

是否使用 NOT NULL 應根據業務需求來決定。對於關鍵欄位(如使用者名稱、訂單 ID 等),NOT NULL 可以保證資料完整性。而對於可選欄位或表示狀態的欄位(如發貨日期、可選資訊等),允許 NULL 可能會提供更大的靈活性。

6. 版本管理和演化中的資料庫設計

在長期的專案中,資料庫架構會隨著業務需求的變化而演化。有時,允許 NULL 可以為將來未預見的擴充套件提供靈活性。

示例:產品升級和新功能場景

  • 欄位discount_rate
  • 分析:假設在初期設計中,所有產品都沒有折扣,因此 discount_rate 欄位在資料庫設計時不需要。但是隨著業務的擴充套件,某些產品開始引入折扣機制。在這種情況下,最初的產品可能沒有折扣值,因此可以讓 discount_rate 允許為 NULL,表示未使用折扣。
CREATE TABLE products (
    id INT PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    price DECIMAL(10, 2) NOT NULL,
    discount_rate DECIMAL(5, 2)  -- 允許為 NULL,表示無折扣
);

影響:

  • 演化靈活性:允許 NULL 為未來的擴充套件提供了更多空間,避免了在設計初期過度限制。
  • 程式碼維護:開發團隊可以根據業務需求逐步新增新功能,而不必在每次迭代時重新設計表結構。

7. 基於資料統計和分析的設計

在一些資料統計場景中,允許欄位為 NULL 可以提供更清晰的資料檢視,尤其是針對資料缺失的處理。NULL 明確表示資料不存在,而非無意義的預設值。

示例:使用者活動追蹤

  • 欄位last_purchase_date
  • 分析:在使用者行為分析中,追蹤使用者最後一次購買的日期是常見需求。如果使用者從未購買過,last_purchase_date 可以為 NULL。使用 NULL 而不是使用虛假的預設值(如 '1970-01-01')能更準確地反映業務狀態。
CREATE TABLE user_activity (
    user_id INT PRIMARY KEY,
    last_login DATE NOT NULL,
    last_purchase_date DATE  -- 可以為 NULL,表示沒有購買記錄
);

影響:

  • 資料準確性:NULL 比預設值更好地表達“未發生”這一狀態,避免使用錯誤資料進行分析。
  • 分析效率:透過 IS NULL 判斷可以快速篩選出未進行過某些行為的使用者。

8. 遷移和資料相容性

在進行資料庫遷移或者不同系統之間的資料整合時,允許 NULL 通常能提升相容性,尤其在早期設計和目標系統不一致的情況下。如果源系統的資料允許某些欄位為空,而目標系統不允許 NULL,那麼遷移過程中可能會遇到問題。

示例:跨系統資料遷移

  • 欄位address_line_2
  • 分析:假設一個系統將使用者地址劃分為 address_line_1address_line_2,其中 address_line_2 是可選的。而在目標系統中,address_line_2 使用 NOT NULL 會導致部分資料遷移失敗。因此,在這種情況下,設計時應允許 NULL,確保資料相容性。
CREATE TABLE user_addresses (
    user_id INT PRIMARY KEY,
    address_line_1 VARCHAR(255) NOT NULL,
    address_line_2 VARCHAR(255)  -- 允許 NULL,因為不是所有使用者都需要填寫
);

影響:

  • 遷移成功率:允許 NULL 可以提高資料遷移的相容性,確保不同系統的資料能夠無縫整合。
  • 資料對映:避免強制使用預設值或虛假資料來填補空缺,提高資料的準確性。

9. 使用 NOT NULL 和預設值的結合

在一些情況下,使用 NOT NULL 並搭配預設值可以提高欄位的健壯性,避免開發人員在插入資料時遺漏某些資訊。例如,對於布林型別欄位或者列舉型別欄位,通常透過 NOT NULL 和預設值確保邏輯上的完整性。

示例:訂單狀態管理

  • 欄位order_status
  • 分析:在訂單管理系統中,訂單狀態是必不可少的欄位。為了確保每個訂單在建立時都具有明確的狀態,使用 NOT NULL 並設定預設值(如 "Pending")可以避免遺漏。
CREATE TABLE orders (
    id INT PRIMARY KEY,
    order_date DATE NOT NULL,
    order_status VARCHAR(50) NOT NULL DEFAULT 'Pending'
);

影響:

  • 業務健壯性:透過 NOT NULL 和預設值結合,確保業務中的每個流程都具備初始狀態,避免邏輯錯誤。
  • 易於維護:預設值減少了資料插入時的複雜性,確保系統一致性。

10. 動態資料結構和 JSON 型別

在現代資料庫設計中,使用 JSON 型別儲存不規則或動態資料的情況越來越常見。對於這種場景,是否使用 NOT NULL 的決策與傳統的關係型欄位設計不同。在大部分情況下,JSON 欄位是靈活的,可為空,以適應多樣化的資料格式。

示例:使用者偏好設定

  • 欄位preferences
  • 分析:假設我們需要儲存使用者偏好設定,但不同使用者的偏好種類和數量都不一致。使用 JSON 型別可以靈活儲存這些資訊。允許 NULL 可以處理未提供偏好的使用者資料。
CREATE TABLE user_settings (
    user_id INT PRIMARY KEY,
    preferences JSON  -- 允許 NULL 表示使用者未設定偏好
);

影響:

  • 靈活性:使用 NULLJSON 型別的組合,可以處理動態且不規則的資料結構,適應不同使用者的需求。
  • 可擴充套件性:在後續的需求變化中,不必修改表結構,直接透過更新 JSON 資料即可。

總結一下:如何平衡 NOT NULLNULL

在決定是否使用 NOT NULL 時,應該考慮以下幾點:

  • 業務需求:關鍵業務邏輯需要強制值的欄位應使用 NOT NULL,而可選項和邊緣情況允許 NULL

  • 資料準確性NULL 能夠更好地表達“未知”或“未發生”的狀態,而非使用無意義的預設值。

  • 效能與維護性NOT NULL 可以最佳化查詢效能,減少資料庫索引負擔,但在允許 NULL 的情況下,靈活性和相容性會更高。

  • 未來的擴充套件:在設計初期,應考慮到未來業務的演化,過早限制欄位為 NOT NULL 可能會在擴充套件時帶來挑戰。

  • 使用 NOT NULL 的場景:關鍵業務欄位、資料一致性要求高、頻繁查詢的欄位。

  • 允許 NULL 的場景:可選欄位、表示未知狀態、靈活關聯關係。

所以呢,根據業務場景合理使用 NOT NULL,可以在保持資料完整性的同時提供必要的靈活性和效能最佳化。資料庫設計的時候,我們可以在靈活性、效能和資料完整性之間找到平衡。

相關文章