一个简单的场景:
业务需要两个对象:学生和教师,两者均含姓名、性别、生日等大量重复属性,但是学生有个“学号”属性,教师有个“科目”属性,如果将这两者同时设计成一张 user 表,那么非这两个属性的拥有者的该字段将为 null 值,而且需要设立一个 role 字段以区分两者身份。
但是如果如果分开设计,那么大多数属性都是重复的,这样似乎较为浪费空间,而且业务层多了 “判断用户角色” 的逻辑代码
怎样设计表才是最佳实践呢?
1
katsusan 2019-01-09 00:02:43 +08:00 via iPhone
分开设计为什么会浪费空间呢,而且将来用户量大的时候用户类型也是水平拆分的手段之一吧,个人理解。
|
2
Darren9276 2019-01-09 00:09:21 +08:00 via Android
我的理解是如果老师的权限只是学生权限的超集,那么设计成一个表,添加角色以区分,如果两者的权限几乎不重叠那么设计成两个表也无所谓,不过还是推荐前者
|
3
qiayue 2019-01-09 00:09:24 +08:00
现在都什么年代了,硬盘不值钱了,别想着省空间
|
4
liunull 2019-01-09 00:09:57 +08:00 via Android
你合在一起才浪费空间吧
|
5
lhx2008 2019-01-09 00:10:01 +08:00 via Android
拆一个公共表就行了
公共表是含义是成员,有每个人的基础信息 然后再是教师表和学生表,每个教师引用一个成员的 id,然后再添加老师专用字段 |
6
lhx2008 2019-01-09 00:11:28 +08:00 via Android
对应到编程语言就是父类和子类的概念
|
7
lhx2008 2019-01-09 00:12:13 +08:00 via Android
mysql 官方例子就是这么搞的
|
8
zjp 2019-01-09 00:12:49 +08:00
非要用将学生和教师视为一种特定 user 可以用数据库的表继承
|
9
saulshao 2019-01-09 00:20:30 +08:00
应该把共有的字段拆出来,变成一个表,然后用关联到学生和老师 2 个表上。
|
10
dumbunny 2019-01-09 00:23:20 +08:00 via Android
直接两张表不就挺好的
|
11
d3vil 2019-01-09 00:24:28 +08:00 2
分开表,并不会占用更多的空间,因为每个人就是占用一条记录;
不分开表,也就是楼主所说的前者的实现方式,这是最 Low 的实现方式,弊病你自己也明白,不仅仅会造成空字段,并且会在写代码上造成不方便; #5 楼所说的父表与子表的方案,这个方案在逻辑上是最具有层级关系的,就是它在逻辑性、系统性这两个方面来说,是最好的,最规范的。(适合大型、复杂、非常需要考虑扩展性的业务),但是他的方案不管是在空间上、时间上(查询),都是最为难受的选择,想要整个系统的高要求,不管是空间上还是时间上,你都无法避免需要进行牺牲,所以,楼主的问题其实应该自己告诉自己,因为你最明白你这个业务的预算是多少,做一个工程,你自己得做评估、预算,有了这个,别人才能够根据你的实际情况来推荐具体的解决方案,否则,便是无源之水,无根之木。 |
12
d3vil 2019-01-09 00:29:28 +08:00
所以初步的结果就是:
如果楼主的业务如果是中型以下,可以选择设计两个表,也就是学生一张表,教室一张表。 如果楼主的业务如果是中型以上,那么则应该规范设计,将公共字段独立为一张表,然后每一个 Role 去创建一个新表来存储 its own distinct filed。 |
13
d3vil 2019-01-09 00:35:46 +08:00
#12 打错字了,教室改为“教师”。想在中型以下那一行多加一句:如果业务不大,其实设计两张表真的是最优选择,多一个 Table 真不会多占用多少空间= =好好想想吧,怎么会有“分开设计,那么大多数属性都是重复的,这样似乎较为浪费空间”这种说法?道理在哪里?赐教一下?这到底怎么浪费空间了?只是另外腾出一个动态空间来存放东西而已,只是多贴了一个标签而已,多少条记录就是多少条记录的空间啊,就是多了一个“标签”的空间而已啊,不过遥想我们当初学 SQL SERVER 2008 的时候,创建一个数据库好像要为两个文件(记得好像是 mdb 和 log 文件)给定一个固定的大小....MySQL 应该不存在这样的吧?空间都是动态增长、占用的吧?
|
14
lhx2008 2019-01-09 00:45:02 +08:00 via Android
@d3vil
写的不错哈哈。 一张表肯定不行的,无法应对业务的变化 两张表原则来说问题也不大,但是主要也是考虑到业务的实际情况。比如到时候学生和老师都要登录,那是不是还要加一个 id 判断的逻辑?或者由用户自己选择自己是老师还是学生,这样都是不友好的。所以本质上还是抽出一个成员的概念应对业务变化。 至于性能真的不是问题,就是一个简单的连表查询,主键索引速度很快的。还没到谈到什么预算的问题。不过具体到 ORM 可能配置起来还是有一点点麻烦。 |
15
msg7086 2019-01-09 00:47:46 +08:00
一种做法,STI。
另一种做法,公共表(例如 UserProfile 表同时被 Student 和 Teacher 引用)。 |
16
d3vil 2019-01-09 00:58:31 +08:00
“ id 判断的逻辑”是什么?不同的角色登录,去操作不同的表就可以了,然后让前端后端在代码上配合做区分,我并不认同你所说的“不友好的”,因为我就举个例子吧,阿里云有免费邮箱和企业邮箱,他们都是邮箱吧?但是他们的登录地址却不同,一个是 mail.aliyun.com ,一个是 qiye.aliyun.com ,用户登录不同的邮箱竟然还要跨子域耶!这是不是也是不友好呢?我觉得不能有这样的强迫症= =。
你要想在前端做好看一点,不要有类似上面的选择框,你就可以学阿里邮箱用不同子域管理不同的角色的业务(学生和老师的职能、业务的确是有很大的不同的),要么你就在前端做区分,对一个问题钻牛角尖,只会让自己手足无措的,而永远无法到达一个彼岸的,也没有一个实际的方案能给到你。 简单的连表查询...一个再简单的操作,面对起以后业务的量级,都会有极明显的差异,比如一个表有三百万的数据,你要进行三百万条记录的 Join,这和单表查询的绝对耗时能比吗?肯定会有性能差异的。 那没谈到预算的问题,那就什么方案都行呗,那不就是做外包一样的,怎样实现来得快来得简单,就怎样做,那随便怎么做都行的,实现的方法那么多种,随便选个呗,要是有预算,那就不一样了。要是你不考虑性能,那你就往规范的方向做呗,选择规范设计。 |
17
lrxiao 2019-01-09 05:00:39 +08:00
额 不能 IS-A 一样做 weak entity 吗
|
18
reus 2019-01-09 08:29:36 +08:00
三个表,users, students, teachers,后面两个表里有个 user_id
|
19
lhx2008 2019-01-09 09:02:46 +08:00 via Android
@d3vil
不跟你谈表的问题了。 首先,哪有业务量级呀,一个学校能有多少 QPS,单机都随便跑了。就算有再多钱,也要分析下具体情况,事实上,大部分业务都做不到大公司的量级。就算没有钱,没啥访问量的话,一个 join 查询也不能用了吗? 我也没有否认可以做到微信量级的可能。即使未来达到大公司量级,我们还有读写分离,集群,缓存这么多方法应对量级的增长,单单只是少用一个 join 我觉得有点削足适履了。 join 的性能没有这么差,特别是这种一对一关系,先不说有没有 300 万条,就算有 300 万条,如果你了解数据库的 b+树,就知道你指定一个 id 查是很快的,join 不过是以很快的速度再用 id 查一遍。而且数据库大多都有缓存池优化,包括用 hash 表。 设计规范只是为了将来好改动,如果是自己的项目,自己怎么爽怎么来。 |