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

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

仁风H 仁风H
1 年前

本文首发于知乎专栏,转载请注明出处 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日
北京

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Java中,可以使用NI-VISA库来进行GPIB接口控制,下面是一个简单的示例代码。 ```java import com.sun.jna.Native; import com.sun.jna.Pointer; import com.sun.jna.ptr.IntByReference; import visa32.*; public class GPIBControl { private static final int GPIB_ADDRESS = 1; // GPIB地址 private static final int BUFFER_SIZE = 1024; // 缓冲区大小 public static void main(String[] args) { // 初始化NI-VISA库 VisaLibrary visaLibrary = (VisaLibrary) Native.loadLibrary("visa32", VisaLibrary.class); visaLibrary.viOpenDefaultRM(); // 打开GPIB设备 String deviceName = "GPIB0::" + GPIB_ADDRESS + "::INSTR"; Pointer instrument = new Memory(GpibLibrary.ViUInt32.SIZE); visaLibrary.viOpen(visaLibrary.getResourceManager(), deviceName, new GpibLibrary.ViAccessMode(GpibLibrary.VI_NO_LOCK), new GpibLibrary.ViUInt32(0), instrument); Pointer session = instrument.getPointer(0); // 设置超时时间 visaLibrary.viSetAttribute(new GpibLibrary.ViSession(session), new GpibLibrary.ViAttrState(GpibLibrary.VI_TMO_VALUE), new GpibLibrary.ViUInt32(5000)); // 写入命令 String command = "*IDN?"; visaLibrary.viWrite(new GpibLibrary.ViSession(session), command.getBytes(), new GpibLibrary.ViUInt32(command.length()), new IntByReference()); // 读取响应 byte[] buffer = new byte[BUFFER_SIZE]; visaLibrary.viRead(new GpibLibrary.ViSession(session), buffer, new GpibLibrary.ViUInt32(BUFFER_SIZE), new IntByReference()); String response = new String(buffer).trim(); System.out.println("Instrument response: " + response); // 关闭设备 visaLibrary.viClose(new GpibLibrary.ViSession(session)); visaLibrary.viClose(visaLibrary.getResourceManager()); } } ``` 这个示例代码使用NI-VISA库来打开GPIB设备,设置超时时间,写入命令并读取响应。需要注意的是,GPIB_ADDRESS变量需要设置为实际的GPIB地址,BUFFER_SIZE变量可以根据需要进行调整。 在实际应用中,需要根据仪器的通信协议进行编写代码,例如使用SCPI命令进行控制。希望这个示例代码能够对你有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值