背景
以最常见的电商系统作为例子,电商系统中有商品这样的实体。在数据库设计中,通常我们会使用一个商品表来表示商品实体,但不同类型的商品拥有不同的属性,导致商品表的字段设计成为一个问题。与此同时,前端页面在开发商品表单时,由于商品属性是不确定的,无法预先定义好要显示的表单项,各表单项的显示名称、属性名以及表单控件需要由后端提供该商品类型的模式数据来确定
需求案例
我们通过一个简单的需求案例来说明各种可能的设计方案
同样以电商系统中的商品为例,假设有两类商品,分别为饮料和图书。两类商品都有名称这个属性,饮料另有包装属性,而图书另有页数属性
设计方案
固定大宽表
商品表中预留足够的备用属性,备用属性的含义根据商品类型的不同而改变
商品类型表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 |
PREVIOUSMaven常用命令
NEXTList与数组之间的转化