關(guān)于建表字段是否該使用not null這個(gè)問題你怎么看_

2024-12-17 15:20 更新

大家好,我是 V 哥,在數(shù)據(jù)庫(kù)設(shè)計(jì)中,是否使用 NOT NULL 是一個(gè)非常重要的決策,直接影響數(shù)據(jù)完整性、查詢性能以及業(yè)務(wù)邏輯的復(fù)雜度。使用 NOT NULL 的關(guān)鍵在于理解業(yè)務(wù)需求和具體場(chǎng)景。

下面V哥通過一些場(chǎng)景來分析什么時(shí)候應(yīng)該使用 NOT NULL,什么時(shí)候允許 NULL。一起聊聊經(jīng)驗(yàn)之談,望和兄弟們討論。

1. 必須存在值的字段

對(duì)于某些關(guān)鍵字段,如果業(yè)務(wù)邏輯要求它們始終具有值,那么應(yīng)該使用 NOT NULL 約束。這樣可以防止數(shù)據(jù)不完整,避免潛在的業(yè)務(wù)問題。

示例:用戶注冊(cè)場(chǎng)景

  • 字段username、email
  • 分析:用戶在注冊(cè)時(shí),用戶名和郵箱是必須的。缺少這兩個(gè)字段會(huì)導(dǎo)致后續(xù)的登錄或通知無法進(jìn)行,因此這些字段應(yīng)該設(shè)置為 NOT NULL。

CREATE TABLE users (
    id INT PRIMARY KEY,
    username VARCHAR(255) NOT NULL,
    email VARCHAR(255) NOT NULL
);

影響:

  • 數(shù)據(jù)完整性:確保任何用戶都具備用戶名和郵箱。
  • 性能優(yōu)化:NOT NULL 字段可以優(yōu)化數(shù)據(jù)庫(kù)的索引和查詢效率。

2. 可選字段

有些字段是可選的,它們的缺失不會(huì)對(duì)業(yè)務(wù)邏輯造成影響。在這種情況下,允許 NULL 可以為業(yè)務(wù)提供靈活性,避免強(qiáng)制要求用戶提供所有信息。

示例:用戶信息場(chǎng)景

  • 字段middle_name、profile_picture
  • 分析:中間名和個(gè)人資料圖片通常是可選信息。如果強(qiáng)制使用 NOT NULL,則需要提供默認(rèn)值,這在某些情況下不太合理。比如用戶不提供個(gè)人資料圖片,字段可以設(shè)置為 NULL。

CREATE TABLE user_profiles (
    id INT PRIMARY KEY,
    middle_name VARCHAR(255),
    profile_picture VARCHAR(255)
);

影響:

  • 靈活性:允許字段為空為用戶提供更多自由,同時(shí)保持?jǐn)?shù)據(jù)結(jié)構(gòu)的靈活性。
  • 業(yè)務(wù)適應(yīng)性:隨著業(yè)務(wù)需求的變化,允許 NULL 的字段可以容納更多的邊緣情況。

3. 需要標(biāo)識(shí)未知狀態(tài)的字段

在某些業(yè)務(wù)場(chǎng)景中,NULL 可以表示“未知”或“未提供”的狀態(tài),而不僅僅是“空值”。這種情況下,允許 NULL 是合理的,因?yàn)樗苊鞔_區(qū)分“沒有值”和“值為空”。

示例:訂單處理場(chǎng)景

  • 字段shipped_date
  • 分析:訂單的發(fā)貨日期在創(chuàng)建時(shí)可能未知,只有在訂單發(fā)貨后才會(huì)填入。如果使用 NULL 表示未發(fā)貨狀態(tài),可以簡(jiǎn)化業(yè)務(wù)邏輯,并且更符合直觀的業(yè)務(wù)需求。

CREATE TABLE orders (
    id INT PRIMARY KEY,
    order_date DATE NOT NULL,
    shipped_date DATE
);

影響:

  • 狀態(tài)表達(dá):NULL 可以清晰地表示某個(gè)狀態(tài)未發(fā)生,如訂單尚未發(fā)貨。
  • 簡(jiǎn)化業(yè)務(wù)邏輯:不用添加額外的布爾字段來表示是否發(fā)貨。

4. 外鍵字段和 NOT NULL

外鍵的設(shè)計(jì)中,是否使用 NOT NULL 依賴于業(yè)務(wù)邏輯。強(qiáng)制 NOT NULL 意味著關(guān)聯(lián)關(guān)系是強(qiáng)制性的;允許 NULL 則表示某些記錄可能暫時(shí)沒有關(guān)聯(lián)項(xiàng)。

示例:博客文章和作者

  • 字段author_id
  • 分析:如果業(yè)務(wù)邏輯允許某些文章沒有具體作者(如系統(tǒng)自動(dòng)生成),那么 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
);

影響:

  • 強(qiáng)制關(guān)聯(lián)關(guān)系:使用 NOT NULL 可以確保數(shù)據(jù)的完整性和一致性。
  • 靈活關(guān)聯(lián):允許 NULL 的外鍵字段為業(yè)務(wù)提供更多靈活性,支持特殊場(chǎng)景。

5. 性能與存儲(chǔ)開銷

從性能角度來看,NOT NULL 字段在某些情況下可以加速查詢。因?yàn)閿?shù)據(jù)庫(kù)可以更有效地處理不允許 NULL 的字段,不需要對(duì) NULL 值進(jìn)行額外的判斷。然而,如果太多字段都設(shè)置為 NOT NULL,則可能導(dǎo)致業(yè)務(wù)復(fù)雜性增加。

示例:高頻查詢字段

  • 字段last_login
  • 分析:對(duì)于用戶的登錄系統(tǒng),查詢最后登錄時(shí)間是常見操作。如果該字段被設(shè)置為 NOT NULL,數(shù)據(jù)庫(kù)可以更快地檢索數(shù)據(jù)。

CREATE TABLE user_sessions (
    id INT PRIMARY KEY,
    user_id INT NOT NULL,
    last_login TIMESTAMP NOT NULL
);

是否使用 NOT NULL 應(yīng)根據(jù)業(yè)務(wù)需求來決定。對(duì)于關(guān)鍵字段(如用戶名、訂單 ID 等),NOT NULL 可以保證數(shù)據(jù)完整性。而對(duì)于可選字段或表示狀態(tài)的字段(如發(fā)貨日期、可選信息等),允許 NULL 可能會(huì)提供更大的靈活性。

6. 版本管理和演化中的數(shù)據(jù)庫(kù)設(shè)計(jì)

在長(zhǎng)期的項(xiàng)目中,數(shù)據(jù)庫(kù)架構(gòu)會(huì)隨著業(yè)務(wù)需求的變化而演化。有時(shí),允許 NULL 可以為將來未預(yù)見的擴(kuò)展提供靈活性。

示例:產(chǎn)品升級(jí)和新功能場(chǎng)景

  • 字段discount_rate
  • 分析:假設(shè)在初期設(shè)計(jì)中,所有產(chǎn)品都沒有折扣,因此 discount_rate 字段在數(shù)據(jù)庫(kù)設(shè)計(jì)時(shí)不需要。但是隨著業(yè)務(wù)的擴(kuò)展,某些產(chǎn)品開始引入折扣機(jī)制。在這種情況下,最初的產(chǎn)品可能沒有折扣值,因此可以讓 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 為未來的擴(kuò)展提供了更多空間,避免了在設(shè)計(jì)初期過度限制。
  • 代碼維護(hù):開發(fā)團(tuán)隊(duì)可以根據(jù)業(yè)務(wù)需求逐步添加新功能,而不必在每次迭代時(shí)重新設(shè)計(jì)表結(jié)構(gòu)。

7. 基于數(shù)據(jù)統(tǒng)計(jì)和分析的設(shè)計(jì)

在一些數(shù)據(jù)統(tǒng)計(jì)場(chǎng)景中,允許字段為 NULL 可以提供更清晰的數(shù)據(jù)視圖,尤其是針對(duì)數(shù)據(jù)缺失的處理。NULL 明確表示數(shù)據(jù)不存在,而非無意義的默認(rèn)值。

示例:用戶活動(dòng)追蹤

  • 字段last_purchase_date
  • 分析:在用戶行為分析中,追蹤用戶最后一次購(gòu)買的日期是常見需求。如果用戶從未購(gòu)買過,last_purchase_date 可以為 NULL。使用 NULL 而不是使用虛假的默認(rèn)值(如 '1970-01-01')能更準(zhǔn)確地反映業(yè)務(wù)狀態(tài)。

CREATE TABLE user_activity (
    user_id INT PRIMARY KEY,
    last_login DATE NOT NULL,
    last_purchase_date DATE  -- 可以為 NULL,表示沒有購(gòu)買記錄
);

影響:

  • 數(shù)據(jù)準(zhǔn)確性:NULL 比默認(rèn)值更好地表達(dá)“未發(fā)生”這一狀態(tài),避免使用錯(cuò)誤數(shù)據(jù)進(jìn)行分析。
  • 分析效率:通過 IS NULL 判斷可以快速篩選出未進(jìn)行過某些行為的用戶。

8. 遷移和數(shù)據(jù)兼容性

在進(jìn)行數(shù)據(jù)庫(kù)遷移或者不同系統(tǒng)之間的數(shù)據(jù)整合時(shí),允許 NULL 通常能提升兼容性,尤其在早期設(shè)計(jì)和目標(biāo)系統(tǒng)不一致的情況下。如果源系統(tǒng)的數(shù)據(jù)允許某些字段為空,而目標(biāo)系統(tǒng)不允許 NULL,那么遷移過程中可能會(huì)遇到問題。

示例:跨系統(tǒng)數(shù)據(jù)遷移

  • 字段address_line_2
  • 分析:假設(shè)一個(gè)系統(tǒng)將用戶地址劃分為 address_line_1address_line_2,其中 address_line_2 是可選的。而在目標(biāo)系統(tǒng)中,address_line_2 使用 NOT NULL 會(huì)導(dǎo)致部分?jǐn)?shù)據(jù)遷移失敗。因此,在這種情況下,設(shè)計(jì)時(shí)應(yīng)允許 NULL,確保數(shù)據(jù)兼容性。

CREATE TABLE user_addresses (
    user_id INT PRIMARY KEY,
    address_line_1 VARCHAR(255) NOT NULL,
    address_line_2 VARCHAR(255)  -- 允許 NULL,因?yàn)椴皇撬杏脩舳夹枰顚?);

影響:

  • 遷移成功率:允許 NULL 可以提高數(shù)據(jù)遷移的兼容性,確保不同系統(tǒng)的數(shù)據(jù)能夠無縫整合。
  • 數(shù)據(jù)映射:避免強(qiáng)制使用默認(rèn)值或虛假數(shù)據(jù)來填補(bǔ)空缺,提高數(shù)據(jù)的準(zhǔn)確性。

9. 使用 NOT NULL 和默認(rèn)值的結(jié)合

在一些情況下,使用 NOT NULL 并搭配默認(rèn)值可以提高字段的健壯性,避免開發(fā)人員在插入數(shù)據(jù)時(shí)遺漏某些信息。例如,對(duì)于布爾類型字段或者枚舉類型字段,通常通過 NOT NULL 和默認(rèn)值確保邏輯上的完整性。

示例:訂單狀態(tài)管理

  • 字段order_status
  • 分析:在訂單管理系統(tǒng)中,訂單狀態(tài)是必不可少的字段。為了確保每個(gè)訂單在創(chuàng)建時(shí)都具有明確的狀態(tài),使用 NOT NULL 并設(shè)定默認(rèn)值(如 "Pending")可以避免遺漏。

CREATE TABLE orders (
    id INT PRIMARY KEY,
    order_date DATE NOT NULL,
    order_status VARCHAR(50) NOT NULL DEFAULT 'Pending'
);

影響:

  • 業(yè)務(wù)健壯性:通過 NOT NULL 和默認(rèn)值結(jié)合,確保業(yè)務(wù)中的每個(gè)流程都具備初始狀態(tài),避免邏輯錯(cuò)誤。
  • 易于維護(hù):默認(rèn)值減少了數(shù)據(jù)插入時(shí)的復(fù)雜性,確保系統(tǒng)一致性。

10. 動(dòng)態(tài)數(shù)據(jù)結(jié)構(gòu)和 JSON 類型

在現(xiàn)代數(shù)據(jù)庫(kù)設(shè)計(jì)中,使用 JSON 類型存儲(chǔ)不規(guī)則或動(dòng)態(tài)數(shù)據(jù)的情況越來越常見。對(duì)于這種場(chǎng)景,是否使用 NOT NULL 的決策與傳統(tǒng)的關(guān)系型字段設(shè)計(jì)不同。在大部分情況下,JSON 字段是靈活的,可為空,以適應(yīng)多樣化的數(shù)據(jù)格式。

示例:用戶偏好設(shè)置

  • 字段preferences
  • 分析:假設(shè)我們需要存儲(chǔ)用戶偏好設(shè)置,但不同用戶的偏好種類和數(shù)量都不一致。使用 JSON 類型可以靈活存儲(chǔ)這些信息。允許 NULL 可以處理未提供偏好的用戶數(shù)據(jù)。

CREATE TABLE user_settings (
    user_id INT PRIMARY KEY,
    preferences JSON  -- 允許 NULL 表示用戶未設(shè)置偏好
);

影響:

  • 靈活性:使用 NULLJSON 類型的組合,可以處理動(dòng)態(tài)且不規(guī)則的數(shù)據(jù)結(jié)構(gòu),適應(yīng)不同用戶的需求。
  • 可擴(kuò)展性:在后續(xù)的需求變化中,不必修改表結(jié)構(gòu),直接通過更新 JSON 數(shù)據(jù)即可。

總結(jié)一下:如何平衡 NOT NULLNULL

在決定是否使用 NOT NULL 時(shí),應(yīng)該考慮以下幾點(diǎn):

  • 業(yè)務(wù)需求:關(guān)鍵業(yè)務(wù)邏輯需要強(qiáng)制值的字段應(yīng)使用 NOT NULL,而可選項(xiàng)和邊緣情況允許 NULL
  • 數(shù)據(jù)準(zhǔn)確性NULL 能夠更好地表達(dá)“未知”或“未發(fā)生”的狀態(tài),而非使用無意義的默認(rèn)值。
  • 性能與維護(hù)性NOT NULL 可以優(yōu)化查詢性能,減少數(shù)據(jù)庫(kù)索引負(fù)擔(dān),但在允許 NULL 的情況下,靈活性和兼容性會(huì)更高。
  • 未來的擴(kuò)展:在設(shè)計(jì)初期,應(yīng)考慮到未來業(yè)務(wù)的演化,過早限制字段為 NOT NULL 可能會(huì)在擴(kuò)展時(shí)帶來挑戰(zhàn)。

  • 使用 NOT NULL 的場(chǎng)景:關(guān)鍵業(yè)務(wù)字段、數(shù)據(jù)一致性要求高、頻繁查詢的字段。
  • 允許 NULL 的場(chǎng)景:可選字段、表示未知狀態(tài)、靈活關(guān)聯(lián)關(guān)系。

所以呢,根據(jù)業(yè)務(wù)場(chǎng)景合理使用 NOT NULL,可以在保持?jǐn)?shù)據(jù)完整性的同時(shí)提供必要的靈活性和性能優(yōu)化。數(shù)據(jù)庫(kù)設(shè)計(jì)的時(shí)候,我們可以在靈活性、性能和數(shù)據(jù)完整性之間找到平衡。

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)