【轉】數據庫表結構設計一點心得和經驗

原文:https://juejin.cn/post/7108525565157589005

一個好的表結構設計能減少不小開發量,也能提升部分擴展性,只梳理下自己日常設計表結構的時候一點點心得,經驗

1.0 基本要求

  • 命名規範,命名可讀,同一業務模塊用相同前綴
  • 做邏輯刪除,不做物理刪除
  • 不搞主外鍵關聯,在程序業務邏輯中維護

2.0 不需要嚴格遵守 3NF,通過業務字段冗餘來減少表關聯

通俗地理解三個範式,對於數據庫設計大有好處。在數據庫設計中,爲了更好地應用三個範式,就必須通俗地理解三個範式(通俗地理解是夠用的理解,並不是最科學最準確的理解):

  • 第一範式:1NF 是對屬性的原子性約束,要求屬性具有原子性,不可再分解
  • 第二範式:2NF 是對記錄的惟一性約束,要求記錄有惟一標識,即實體的惟一性
  • 第三範式:3NF 是對字段冗餘性的約束,即任何字段不能由其他字段派生出來,它要求字段沒有冗餘

沒有冗餘的數據庫設計可以做到。但是,沒有冗餘的設計未必是最好的設計

基本表及其字段之間的關係, 應儘量滿足第三範式。但是,滿足第三範式的數據庫設計,往往不是最好的設計。爲了提高數據庫的運行效率,常常需要降低範式標準:適當增加冗餘,達到以空間換時間的目的

img

比如上面這張存放商品的基本表。“金額”這個字段的存在,表明該表的設計不滿足第三範式,因爲“金額”可以由“單價”乘以“數量”得到,說明“金額”是冗餘字段。但是,增加“金額”這個冗餘字段,可以提高查詢統計的速度,這就是以空間換時間的作法

3.0 通用公共字段

  • deleted_at 默認是 null(大部分orm 默認查詢 null)
  • sort 非必需 默認 是 1,頁面展示經常用到
  • version 非必需,默認是 1,樂觀鎖需要
  • created_at 創建時間
  • updated_at 更新時間
  • created_by 創建人
  • updated_by 更新人
  • remark 非必需 備註

4.0 一張表表達多維度基礎信息

以省市區這種樹結構的級聯信息爲例,我們可以設計 3 張表,然後分別關聯,但是我們也可以設計一張表,會更加簡潔,3 張表,設計如下:

// 省表
CREATE TABLE `province` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `code` varchar(6) NOT NULL,
  `name` varchar(20) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=35 DEFAULT CHARSET=utf8;

// 市表
CREATE TABLE `city` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `code` varchar(6) NOT NULL,
  `name` varchar(20) NOT NULL,
  `provincecode` varchar(6) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=343 DEFAULT CHARSET=utf8;
// 縣,區表
CREATE TABLE `area` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `code` varchar(6) NOT NULL,
  `name` varchar(20) NOT NULL,
  `citycode` varchar(6) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=3145 DEFAULT CHARSET=utf8;

這樣是沒問題的,換成一張表設計,如下:

CREATE TABLE `region` (
  `region_id` varchar(10) NOT NULL COMMENT '地區主鍵編號',
  `region_name` varchar(50) NOT NULL COMMENT '地區名稱',
  `region_short_name` varchar(10) DEFAULT NULL COMMENT '地區縮寫',
  `region_code` varchar(20) DEFAULT NULL COMMENT '行政地區編號',
  `region_parent_id` varchar(10) DEFAULT NULL COMMENT '地區父id',
  `region_level` int(2) DEFAULT NULL COMMENT '地區級別 1-省、自治區、直轄市 2-地級市、地區、自治州、盟 3-市轄區、縣級市、縣',
  PRIMARY KEY (`region_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='地區表';

通過 region_level這個字段來增加了數據的一個維度信息,就表達一條數據的信息,然後通過 region_parent_id 來表達關聯關係,這樣一張表就能表達出來了,這樣做查詢的時候,一張表就把所有信息查出來了,方便不少

這種設計用在基礎數據設計上是非常方便的,這種表結構變化比較少,新增,更改不頻繁的表結構上面,比如,分類信息,部門信息

不能濫用,比如學生,班級,就不適合,比如洛可場景,樓層,展位,操作頻繁,而且也不符合三範式

5.0 1:n 設計變通

1對多關係,日常用的也多,例如: 班級和學生,部門和員工,客戶和訂單,分類和商品,一般建表原則: 在從表(多方)創建一個字段,字段作爲外鍵指向主表(一方)的主鍵

img

網上找的圖

但是,有時候我們會遇到複雜一點的一對多關係,我們已營銷領域的常用的實驗爲例,假設我們需要對頁面,不同版本組件,做實驗,按照上面的設計,表結構設計如下:

表結構設計如下: image.png

這樣我們需要在頁面表,組件表新增 實驗id字段,然後在對應 pageService,componentService 新增對應配置方法,隨着時間的推移,大家發現做實驗非常好,幹啥都做個實驗,比如內容,投放計劃,按照上面設計,我們就需要在內容表,投放計劃表都加上實驗id字段,寫對應的配置方法,非常繁瑣變,也不靈活

如果我們新增一張實驗配置表,把 1:n 改造成 1: 1,改造成下面這樣:

image.png

這裏我們新增一張實驗配置表,通過 targer_id,target_type,就把實驗配置信息獨立出來了,不用改動原始的頁面表和組件表,同時在領域設計上來說,我們是把實驗配置信息單獨在自己的領域裏面,這樣在代碼層面,也值需要關注實驗相關的service,接口了

這裏也需要注意,並不是任何時候都需要把1:n, 拆成中間表 1:1,首先需要預判下,你當前做的事情是否和多張表有關聯,有改動是否是很多地方可能用到的,是否歸屬在一個業務模型下面,比如頁面和組件,是個 1:n 的關係,在一個業務模型下,那就沒必要搞個頁面組件中間表,直接在組件表加一個頁面 id 字段就好

6.0 如果 2 張表之間存在 n:n 的關係,應改消除這種關係

消除的辦法是,在兩者之間增加第三張表。這樣,原來 n:n 的關係,現在變爲兩個 1:n 的關係。要將 原來兩個表的的屬性合理地分配 到第 3 張表。一般來講,數據庫設計工具不能識別多對多的關係,但能處理多對多的關係

比如在“圖書館信息系統”中,“圖書”對應一張表,“讀者”也對應一張表。這兩張表之間的關係,是一個典型的多對多關係,一本圖書在不同時間可以被多個讀者借閱,一個讀者又可以借多本圖書,爲此,要在二者之間增加第三個實體,該實體取名爲“借還書”,它的屬性爲:借還時間、借還標誌(0 表示借書,1 表示還書),另外,它還應該有兩個外鍵(“圖書”的主鍵,“讀者”的主鍵),使它能與“圖書”和“讀者”連接

7.0 n:n 消除後的中間表適當冗於字段

原來兩個表的的屬性合理地分配 上這段加粗 大部分時候 大家都會把 n:n 拆開,但是並不是拆出來的中間表就 3 個字段,以上面圖書,讀者爲例,假設中間表就 3個字段

CREATE TABLE `book_reader` (
  `id` varchar(10) NOT NULL COMMENT '主鍵編號',
  `book_id` varchar(50) NOT NULL COMMENT 'book 表 主鍵',
  `reader_id` varchar(10) DEFAULT NULL COMMENT '讀者表主鍵'
  PRIMARY KEY (`region_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='圖書,讀者中間表';

這張表特別符合三範式的,但是你做查詢的時候每次都得關聯 3 張表查詢,其實可以適當冗於部分信息,比如書名,書的 isbn 編號,這樣的好處是,查中間表就能獲取部分信息,但是需要注意的是合理分配, 不要把變動頻繁的字段往中間表放,更新數據的時候,記得更新中間表

8.0 最後

我們前端大部分時候不會遇到很複雜的數據庫操作和表結構設計,基本上理解了 RBAC 用戶權限管理數據庫設計,就能滿足日常的各類設計,RBAPC 模型包含 用戶,角色,權限,菜單,包含 1:n,n:n 關係,非常值得好好學習下

參考資料:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章