数据库杂谈之:如何优雅的进行表结构设计

作者:仁风H
链接:https://zhuanlan.zhihu.com/p/20785905
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。本文首发于知乎专栏,转载请注明出处 zhuanlan.zhihu.com/p/20数据库表结构设计作为后端软件开发不可或缺的一环,是每个后端工程师都会经历的过程。笔者也多次经历过这样的过程,也尝试过多种不同的设计方案,也从一些优秀的框架中学到不少,但并没有发现相关的文章对其进行总结。所以本文尝试把笔者看到的、学到的总结下来,希望对阅读本文的读者有所启发。

表结构设计主要有两个目的,一是让表结构更加的更具有表现力,做到数据库表的自描述,减少注释甚至不使用注释;二是满足系统效率和扩展性的需要,让系统性能更好,后期维护更简单。

本文主要探讨的是如何优雅的设计表结构,让人能够直观的从命名中窥探设计意图,传达设计者的设计目的,让团队成员达成共识,减少沟通成本。本文不讨论表结构设计对性能的影响,也不讨论数据库设计中的范式与反范式设计。本文将从数据库表的命名和字段的命名两个方面展开。

数据库表的命名

使用名词作为表名

仔细想想便可发现,数据库表中存在的所有数据都是现实世界各种操作的结果,它们有的是中间过程结果,有的是最终数据结果。不论怎样,它们是一份一份没有任何动作的,静态的记录。而表本身就是存储这些记录的容器,从这样的层面理解,表名应该采用名词的形式是完全符合逻辑的。

比如我们要设计一个存储用户邀请的表,invitation 就比 invite 更加的优雅。

相关表采用统一前缀

我们知道,大型系统的设计往往按模块或者子系统进行划分,一个一个模块的处理问题,保证模块间的低耦合,模块内的高内聚。数据库表设计也一样,我们可以对相关联的表采用相同的前缀,使开发人员一眼看上去就知道哪几个表是相关的。

比如对于用户基本信息表、用户的详细信息表和用户的微信绑定表如下的命名更可取:

user
user_profile
user_wechat

字段的命名

本节先介绍几个比较通用的原则,使得字段的含义更容易理解,描述性更强,之后进行简单的总结分类,以便让我们明白这些原则背后的逻辑。

使用动词被动形式+描述性后缀

通过前面我们知道,数据库表中的所有记录都是静态的结果性数据,它是由一定的用户操作产生的。那么它是如何产生的?经过什么样的操作产生的呢?在解答之前先看一个例子,下面是一个简单的 article 表结构:

id: integer
title: varchar
content: text
user_id: integer
create_time: timestamp

这样的设计本身是没有问题的,目前用的也很多。这个设计主要的问题是没有体现出 user_id 与这篇文章的关系,需要经过一定的猜测和思考才能得出。create_time 虽然还比较直观,但没有体现出这篇文章实在过去的某个时间创建的。

然后我们在来看修改后的设计:

id: integer
title: varchar
content: text
created_by: integer
created_at: timestamp

通过把 user_id 替换为 created_by、create_time 替换为 created_at,使得我们更容易理解对应的文章是被指定的人在指定的时间创建出来的,而不需要我们的多方猜测或者查阅文档,使得整个表结构的描述性更强。时间区分当前时间和未来时间

英语中表时间的时候, at 一般跟一个时间点,而 in 有表示在未来的某个时间之内的意思。结合起来,笔者倾向于用 at 表示过去或者现在的时间,而用 in 表示未来的时间。比如日历中的 event 有开始时间和结束时间的概念,我觉得如下的命名是比较合理的:

starts_at 事件的开始时间,相对 ends_in 它属于当前时间,采用 _at 后缀
ends_in 事件的结束时间,相对 ends_in 它属于未来时间,从用 _in 后缀

其他我们比较常用的比如 created_at、updated_at、expires_in 都属于这种类型。

使用第三人称单数

当我们采用动词+介词的时候我更倾向与使用第三人称单数,因为字段描述的这个实体是单数的,通过使用第三人称单数,我们可以自然语言的方式表达出需要的意思。比如以 event 为例,翻译成英语是这样的:

The event starts at 2016-05-05 12:00:00

完全符合英语的语法,也表达了我们想要表达的意思。

区分单数与复数

单数与复数主要是对字段内容的描述,比如通知(notification)有接收人这个字段,如果我们需要通知能够发送给多个人,那么 receivers 这样的字段名称明显好于 receiver,因为 receivers 体现了通知可以发送给多个人这一个事实。

前面从四个原则出发介绍了如何让字段更具有描述性,简单总结下来我觉得从语义上来说,字段可以分为两种类型:描述性字段动作性字段

描述性字段是对对应数据库记录(或者说实体)的补充说明,比如以人作为实体,那么人的身高、体重和血压就属于这类描述性字段。

描述性字段如果是动词+介词的形式,动词需要采用第三人称单数的形式,比如 starts_at。然后根据字段的内容,如果内容有多个元素或实体,我们需要使用复数,否则使用单数形式。

动作性字段不仅能对所属实体进行补充说明,还能对这个实体的所涉及操作有所描述。比如我们有 article 这个实体, article 的 created_by 和 created_at 就属于动作性字段,因为它不仅表达了 article 的创建者和创建时间这层信息,它还表达了这个 article 是指定的人被创建的,而不是凭空生成的。

2016年5月5日
北京

数据库杂谈之:如何优雅的进行表结构设计》有一个想法

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注