1. 程式人生 > >關於每一個數據庫表都應該有一個單一的欄位作為主鍵的討論

關於每一個數據庫表都應該有一個單一的欄位作為主鍵的討論

2010年5月6日更新: 只有真正懂得了這個道理的人, 才算真正理解了關係資料庫. 如何才算懂得了這個道理? – 即使你有一百個理由要用關聯主鍵, 你也能找到這唯一的一個理由放棄, 改而使用單一欄位做主鍵.

——

在資料庫設計中, 每一個表都應該有一個欄位作為主鍵. 這個欄位一般是自增整數字段, 或者某些資料庫支援的自動產生不重複字串的欄位, 也可以是程式自己產生的唯一標識. 總之, 每一個數據庫表都應該有一個單一的欄位作為主鍵.

使用單一的欄位作為表的主鍵, 有許多優點. 最重要的一條是可以通過一個原子屬性(如整數, 字串)來標識一行記錄. 一旦可以方便地標識一行記錄, 那麼資料的查詢, 更新, 刪除也都非常簡單.

如果一個表沒有主鍵, 那麼必須通過一行記錄本身才能標識一行記錄. 也就是, 你必須知道一行記錄的每一個欄位的值, 才能在 SQL 語句中操作這條記錄.

資料庫表支援聯合主鍵, 也就是使用表的至少2個欄位作為主鍵. 使用聯合主鍵的缺點和不使用主鍵的缺點有很大的相似性, 因為當聯合主鍵所使用的欄位越多, 聯合主鍵的缺點就越來越趨近於不使用主鍵, 直到表的所有欄位作為主鍵時, 和不使用主鍵幾乎是完全相同的 – 除了前者不能存在重複行,

作為主鍵的欄位一般使用自增整數字段, 因為絕大多數關係型資料庫管理系統都支援這種欄位型別, 而且速度快, 佔用空間少. 一個害怕使用自增整數字段作為主鍵的理由是自增整數會迴繞從而導致重複. 但這種擔心是沒有必要的. 對於最普通的32位無符號整數, 其最大值是在 Mysql 中是 4294967295(43億) , 如果資料庫表以每秒1條的速度儲存記錄, 那麼一天也只能 86400 條(約10萬條)記錄, 需要 49710 天(約136年)之後才能發生重複. 幸運的是, 絕大多數系統產生資料的速度比這個假設慢得多了. 如果系統產生資料的速度確實足夠快速, 則可以使用 64 位的長整型整數.

如果確實需要聯合主鍵, 則可以使用 UNIQUE 索引來代替. 任何不得不使用聯合主鍵的需求, 都是基於對關係資料庫的錯誤理解而產生的. 並不是要用"單一主鍵"來覆蓋"複合主鍵"的需求, 而是"在表設計中, 單一主鍵是必須的, 用聯合唯一索引來覆蓋’複合主鍵’的需求".

一個例子:

表1: 使用聯合主鍵(只是作為反面例子出現, 它應該被聯合唯一索引代替)

table_a{
    field_a: PK;
    field_b: PK;
    field_…: PK
    field_c;
}

表2: 不使用聯合主鍵

table_b{
    id: PK;
    field_a: UNIQUE;
    field_b: UNIQUE;
    field_…: UNIQUE
    field_c;
}

* 聯合主鍵導致資料操作複雜化

這兩種表定義都能反映相同的業務模型. 使用聯合主鍵對儲存資料的程式程式碼影響不大, 情況1通過聯合主鍵保證不會產生重複資料, 但情況2通過聯合唯一索引儲存不會產生重複資料. 但對查詢操作有不良影響, 同時影響到使用了查詢功能的更新和刪除操作. 考慮查詢特定幾條記錄的情況:

表1: 查詢 (field_a, filed_b, …) = [(1, 2, ...), (1, 3, ...), (2, 4, ...), ...] 這幾條記錄.
表2: 查詢 id = [1, 2, 3, ...] 這幾條記錄.

顯然, 表2的描述更簡潔, 從而使程式程式碼和 SQL 語句更簡潔. 如果把所有的欄位都作為主鍵, 那麼表1就不存在查詢特定幾條記錄的功能了, "因為已經知道, 所以不必再找". 這是一種最極端的聯合主鍵的副作用, 聯合主鍵的欄位越多, 就越趨向於這種極端.

* 聯合主鍵不利於表示"關係"

聯合主鍵不利於表示"關係", 是因為它會造成不必要的冗餘. 考慮有另外兩個表, 需要分別關聯 table_a 和 table_b:

table_ac{
    id;
    table_a_field_a;
    table_b_field_b;
    …
}

table_bc{
    id;
    table_b_id;
}

顯然, 不使用聯合主鍵可以減少冗餘. 如果使用所有欄位作為聯合主鍵, 將變成把 table_a 的資料儲存兩份, 一份在 table_a, 另一份包含在 table_ac. 個人認為, 聯合主鍵是是關係資料庫理論一種概念, 在實際中如果使用是"反關係資料庫模型"的, 實際使用應該用聯合唯一索引替代.