实体不同类型拥有不同属性的数据库设计

背景

以最常见的电商系统作为例子,电商系统中有商品这样的实体。在数据库设计中,通常我们会使用一个商品表来表示商品实体,但不同类型的商品拥有不同的属性,导致商品表的字段设计成为一个问题。与此同时,前端页面在开发商品表单时,由于商品属性是不确定的,无法预先定义好要显示的表单项,各表单项的显示名称、属性名以及表单控件需要由后端提供该商品类型的模式数据来确定

需求案例

我们通过一个简单的需求案例来说明各种可能的设计方案

同样以电商系统中的商品为例,假设有两类商品,分别为饮料和图书。两类商品都有名称这个属性,饮料另有包装属性,而图书另有页数属性

设计方案

固定大宽表

商品表中预留足够的备用属性,备用属性的含义根据商品类型的不同而改变

商品类型表t_type

id name
1 饮料
2 图书

商品表t_product

id name type_id attr1 attr2
1 可口可乐 1 瓶装 null
2 三国演义 2 560 null

模式表t_schema

id type_id key show_name component
1 1 attr1 包装 text
2 2 attr1 页数 number

可扩展大宽表

每有一种新属性,则在商品表增加新的字段来表示新属性

商品类型表t_type

id name
1 饮料
2 图书

商品表t_product

id name type_id package page
1 可口可乐 1 瓶装 null
2 三国演义 2 null 560

模式表t_schema

id type_id key show_name component
1 1 package 包装 text
2 2 page 页数 number

属性子表

每一类商品都有自己专属的属性子表

商品类型表t_type

id name attr_table
1 饮料 t_beverage_attr
2 图书 t_book_attr

商品表t_product

id name type_id
1 可口可乐 1
2 三国演义 2

饮料属性表t_beverage_attr

id product_id package
1 1 瓶装

图书属性表t_book_attr

id product_id page
1 2 560

模式表t_schema

id type_id key show_name component
1 1 package 包装 text
2 2 page 页数 number

JSON字段

各商品不同类型的属性,放在同一个字段中,字段数据类型为JSON

商品类型表t_type

id name
1 饮料
2 图书

商品表t_product

id name type_id json_attr
1 可口可乐 1 {"package": "瓶装"}
2 三国演义 2 {"page": 560}

属性元数据表t_attribute_meta

id attr_key attr_name value_type
1 package 包装 varchar
2 page 页数 int

模式表t_schema

id type_id attribute_meta_id show_name component
1 1 1 包装 text
2 2 2 页数 number

属性纵表

各商品不同类型的属性,放在一个属性纵表中

商品类型表t_type

id name
1 饮料
2 图书

商品表t_product

id name type_id
1 可口可乐 1
2 三国演义 2

商品属性表t_attribute

id product_id value attribute_meta_id
1 1 瓶装 1
2 2 560 2

属性元数据表t_attribute_meta

id attr_key attr_name value_type
1 package 包装 varchar
2 page 页数 int

模式表t_schema

id type_id attribute_meta_id show_name component
1 1 1 包装 text
2 2 2 页数 number