数据模型的概念

本文将理解数据模型分为4个阶段。 (为了便于理解,本文不采取专业术语,比如“聚合”会说成“统计”,不说“join”等数据库术语,进阶的知识比如复合主键也会省略不做介绍。)

1. 什么是数据表?

数据表是明确规定了字段类型,每一行数据都严格按照字段顺序与类型记录的表。更进一步的,数据表一般会有一个无意义的、静态的字段来为每条数据编号,这个字段被称为主键字段。在大多数表中,会将主键字段命名为“id”,体现为一个随着数据量自增的整数。
成绩表A
id姓名班级科目成绩
1小明1班语文98
2小明1班数学78
3小明1班英语85
4李雷2班语文88
5李雷2班数学77
6李雷2班英语100
7韩梅梅1班语文30
8韩梅梅1班数学30
9韩梅梅1班英语30
如上表,该表的字段有id、姓名、班级、科目、成绩 5个,其中id为该表主键,姓名、班级、科目是文本字段,成绩为数字字段。表格的9条数据,每一条数据都严格按照这几个字段的先后顺序、类型记录了各字段下的值。比如,成绩中就不允许出现“不合格”这样的文本值。

2. 什么是统计表?数据表和统计表的意义是什么?

统计表是对源数据表进行统计后产生的表,统计表没有主键,其他的记录原则和数据表一致。
比如针对上一节的成绩表,我想知道各班级各同学的总分情况,那么可以得到如下的统计表:
班级姓名总分
1班小明261
1班李雷90
2班韩梅梅265
统计表会有明确的分组字段 (黄色表示) 和统计字段 (无底色表示) ,一个统计字段只能由源数据表的单个字段统计得到,可执行的常见的统计操作一般有计数、求和、求平均、最大值、最小值几种。数据表的信息对于一般人而言太多太杂,提炼出针对性的统计表是非常常见的需求。
对于初学者,一个典型的场景是:在没有任何源数据表的情况下,我接到一个统计需求,我可以直接绕过数据表,直接制作统计表吗?
回答是不建议。统计过程,本质是提炼信息的过程,这个过程会损失信息量。比如仅看总分统计表,我们无法知道每个人各科的成绩是什么,甚至不知道有哪几门科目。这样在下一次遭遇其他的统计需求时,我们可能就无法满足,比如,各科成绩的平均分情况。
因此标准的流程是,先数据采集做出数据表,再通过数据表统计出统计表。

3. 区分什么是“坏的”的数据表

在知道数据表会经常遭遇统计需求后,我们来区分什么是坏的数据表。
成绩表B
id姓名班级语文成绩数学成绩英语成绩
1小明1班987885
2李雷2班8877100
3韩梅梅1班303030
【表B】和【表A】的信息量是一致的,但是为什么要将其称为“坏的”数据表?
因为一般而言,我们会要求数据表在设计好后就尽量少的改动自身的字段结构,表B的设计延展性不够强,会导致遭遇后续的新需求时要对数据表的维护升级工作成本更高。
比如,后续的考试增加一个新科目 “物理”,在【表A】中我只需要在记录每个人的成绩时多记录一条科目=物理的成绩即可,而【表B】我则必须要去为整张表增加一个字段。
更抽象地说:在承载相同信息量且无重复信息的情况下,一张数据表的字段设计,应该以数据条数最大化为原则。

4. 为什么要拆分数据表并建立数据关系?

在上一阶段我们讨论了【表B】是不合格的,这一阶段我们要进一步指出:【表A】其实也是不合格的。
可以假象几种情况:
如果某名同学发生了转班,那么【表A】原先所记录的该同学的班级就是错误的,必须要统一进行修改;
如果某个科目改了名(比如“英语”改为“外语”),那么【表A】的记录同样会出错需要修改;
又如要从【表A】中统计每个班级有几个人,也许可以对姓名去重后,以班级为分组字段统计计数量。但这也未必能得到真实的数据:可能有人缺席了这次考试、可能有人在这次考试后退学/才刚入学。
总之,成绩表尽管有人员字段,但其延展性依然成问题,无法准确记录人员、科目的变化情况。
这个意义上,我们需要拆分数据表,以应对更全面和深入的需求。 (拆分数据表的另一个核心作用是减少调用数据时对服务器的压力,这点不做展开)
比如【表A】就理应被拆分为人员表、科目表以及成绩表 (可以进一步拆分班级表,但因为班级相对比较固定,不进行该操作) 。
人员表person
id姓名班级
1小明1班
2李雷2班
3韩梅梅1班
科目表course
id名称
1语文
2数学
3英语
但是一个问题是,从成绩表拆分出人员、科目后,成绩表现在应该变为什么样?如果没有了姓名、科目字段,要如何确定每个成绩的记录是哪个科目哪个人员的?
——通过建立关系,也就是在成绩表中插入对应的人员表id和科目表id:
成绩表grade
idperson_idcourse_id成绩
11198
21278
31385
42188
52277
623100
73130
83230
93330
我们可以通过person_id查询人员表的信息,通过course_id引用course表的信息,从而让成绩表grade承载的信息量和成绩表A完全一致。
建立关系的本质是使对方表可以引用当前表的数据,而数据模型,就是带有关系的数据表。关系主要以1:N(一对多)关系为主,1:1查漏补缺用。

5. 1:1(一对一)关系

如果表A与表B建立了1:1关系,A表中的某一条数据只跟B表的某一条数据相关联。
比如我们有一张很大的用户信息表,由于字段过多,不方便管理,于是根据业务需求将其拆分成了A、B两张表,其中A表为user表,B表为user_auth表,user表主要存放的字段为用户基本的信息(用户ID、真实姓名、昵称、真实头像、性别、职位、教育程度、专业、创建该用户的时间等),而在user_auth表主要用存储验证用户的信息(用户ID、用户密码、邮箱、手机号等信息。)
1:1关系实际上就是将一张较大的表但是这些数据字段不容再拆分时,拆分成了两张或者多张表,通过1:1的关系相关联,方便数据的查询以及管理。

6. 1:N(一对多)关系

如果A表对B表建立了1:N关系,我们将A称为B的上级表,比如上面的学生表对学生成绩表就建立了1:N关系。
建立关系的本质,是在下级表中插入一个记录上级表id的字段,表示这个下级表条目属于哪个上级表条目,比如学生成绩表的该条数据:
就表示其属于学生表中id=1的条目,也就是林总的成绩。
我们一般将N表中表示1表id的字段称为外键。在zion中建立关系后,会自动在下级表中添加外键。
那么,如何判断两张表是否要建立关系、以及怎么建立关系呢?
- 从属视角
亦即判断:B表条目是否只能从属于一条A表条目,如是,则A表对B表可建立关系。
比如 一个学生只能在一个班级内,而一个班级里有多个学生,那么班级对学生就可以建立1:N关系; 又比如一条学生成绩只能从属于一个学生,而一个学生可以有多条成绩记录,那么学生表对学生成绩表就可以建立1:N关系。
- 引用视角
N表可以通过关系引用到1表的信息。可以将关系看做是一扇门/一个链接,通过这扇门N表可以找到对应1表的信息。 对应在zion,这种引用体现为建立关系后,绑定时出现的侧拉框。比如,我在学生成绩列表中,想知道各条成绩对应的学生的学号:
当涉及复杂数据模型,对关系感到混乱时,也可以从引用的角度进行思考:这张表需要哪些信息?哪部分信息是其他表中就已经有了的?这些信息可以通过关系引用得到,那么怎么建立关系才能做到这点?

7. N:N(多对多)关系

有时我们会遇到两个实体之间存在多对多关系的情况,比如
报名(例:用户表,活动表,用户可以报名多个活动,一个活动也可以被多个用户报名)
收藏(例:用户表,商品表,用户可以收藏多个商品,一个商品可以被多个用户收藏)
点赞(例:用户表,文章表)
这种情况下,我们会采用在两个实体表之间建一个中间表,然后让两个实体表分别对其建立1:N关系,来实现两个实体多对多关系的实现。以活动报名为例:
2022-08-23
5 0