看到同事设计的表结构我人麻了!聊聊怎么更好去设计数据库表

亲身经历

最近接了个外包,和另外两个哥们一起开发。因为他们的时间更充裕,所以前期的表结构都是他们来设计,我没有参与。等我空下来开始做我的部分时,两眼一抹黑,光看表名完全不知道是什么东西。

我不理解,但是大受震撼。

比如说:

复制代码
DC_COURSE_TESTPAPER          课程下的问卷
DC_COURSE_PAPER_HISTORY      问卷与学生的关联表,也就是问卷下发给哪些学生  
DC_COURSE_PAPER_HISTORY_ALL  问卷与问题的关联表,也就是问卷包含哪些问题

光看表名能知道是什么意思吗?难受啊兄弟们。

这让我深刻意识到了:哪怕是一些"高级开发",也并不知道怎么去设计一个好的表结构。

于是决定花点时间写一篇文章,和大家一起探讨如何更好的设计表结构。

所有观点都是我结合多年的经验得来,不一定正确,如有错误之处欢迎大家指正。

表名:第一眼就要知道是干什么的

1. 有意义的前缀 + 清晰的表名

前缀在大型系统中是有必要的,可以区分不同业务模块,但关键是前缀要有明确含义,表名要语义清晰。

❌ 不好的命名

bash 复制代码
DC_COURSE_TESTPAPER      # TESTPAPER是问卷还是试卷?
TB_USER_INFO            # TB前缀无意义,INFO太泛泛
T_ORDER_DTL             # DTL是detail的缩写?
DATA_TBL_001            # 完全看不懂

✅ 好的命名

bash 复制代码
DC_COURSE_QUESTIONNAIRES    # DC表示Distance Course远程课程系统
SYS_USER_PROFILES          # SYS表示系统核心模块
ORDER_ITEMS                # 订单商品明细
LMS_STUDENT_SCORES         # LMS表示Learning Management System

区别在哪里?

  • 不好的:缩写让人猜测,TESTPAPER、INFO、DTL这些词汇模糊不清
  • 好的:前缀有明确业务含义,表名用完整英文词汇表达准确含义

什么时候需要前缀?

  • 多个业务系统共用数据库:USER_, ORDER_, PRODUCT_
  • 区分不同数据类型:LOG_, CONFIG_, TEMP_
  • 大型项目的模块划分:CRM_, ERP_, CMS_

2. 用完整的英文单词而不是拼音

❌ 不好的命名

bash 复制代码
kecheng_wenjuan         # 拼音
user_xinxi             # 中英混合
订单_items             # 中英混合

✅ 好的命名

bash 复制代码
course_questionnaires  # 纯英文,语义清晰
user_profiles         # 纯英文
order_items          # 纯英文

原因

  • 英文是编程的通用语言,团队成员更容易理解
  • 避免编码问题

在一些专业且复杂的业务系统(比如医疗行业)中,会提倡会拼音来作为字段名,但是表名还是英文为主,这个后面展开讲。

3. 表名要体现业务含义,不要只是技术实现

❌ 不好的命名

复制代码
data_table_001
temp_storage
middle_table
relation_mapping

✅ 好的命名

bash 复制代码
student_scores        # 学生成绩
file_uploads         # 文件上传记录
course_enrollments   # 课程报名
user_preferences     # 用户偏好设置

除非是临时用的表,不参与任何业务逻辑,只是用来做数据处理或者测试。

字段命名:见名知意

1. 布尔字段用 is_ 开头

❌ 不好的命名

perl 复制代码
active    # 是激活还是活跃?
delete    # 删除状态还是删除动作?
flag      # 什么标志?

✅ 好的命名

bash 复制代码
is_active     # 是否激活
is_deleted    # 是否已删除  
is_verified   # 是否已验证

也有些团队会用if_作为前缀,这也没什么毛病。

好处

is_ 开头有几个明显的好处:

  • 一眼就能看出是布尔值 - 看到 is_active 就知道这个字段要么是 true 要么是 false,不用再去猜

  • 避免歧义 - 像 active 这样的名字,你搞不清楚它表示的是状态还是动作。is_active 就明确表示状态

  • 代码可读性更好 - 写代码的时候,if (user.is_active)if (user.active) 更容易理解

2. 时间字段统一后缀

❌ 不好的命名

复制代码
create_time
update_date
delete_at
register_datetime

✅ 好的命名

bash 复制代码
created_at    # 创建时间
updated_at    # 更新时间
deleted_at    # 删除时间
registered_at # 注册时间

好处

  1. 一眼就能看出是时间字段 - 看到 created_at 就知道这是时间类型,不用去查表结构

  2. 避免命名混乱 - 有的用 _time,有的用 _date,有的用 _datetime,团队里每个人习惯不一样,最后搞得乱七八糟

  3. _at 在英语里表示"在某个时间点",比 _time 更准确

  4. 逻辑删除的最佳实践 - 特别推荐用 deleted_at 做逻辑删除字段。这样设计有几个好处:

    • 能看出来删除的具体时间
    • 可以追踪删除操作的历史
    • 支持数据恢复(把 deleted_at 设为 NULL 就行)
    • 比用 is_deleted 这种布尔字段更灵活

额外经验

除了基本的命名规范,还有一些实用的经验:

  1. 逻辑删除用时间字段 - 用 deleted_atis_deleted 好,能看出来删除时间,支持数据恢复和历史追踪

  2. 状态字段用枚举 - 不要用数字 1、2、3 表示状态,用 status 字段,值用 'pending''approved''rejected' 这样的英文单词

  3. 金额字段用 decimal - 不要用 floatdouble,用 decimal(10,2) 这样的类型,避免浮点数精度问题

  4. 密码字段要加密 - 密码字段名用 password_hashencrypted_password,不要直接叫 password

  5. 软删除要加索引 - 如果经常查询未删除的数据,给 deleted_at 字段加索引,提高查询性能

3. 外键字段统一 _id 后缀

❌ 不好的命名

bash 复制代码
user          # 这是用户ID还是用户对象?
course        # 课程ID?
teacher_key   # 什么key?

✅ 好的命名

bash 复制代码
user_id       # 用户ID
course_id     # 课程ID
teacher_id    # 教师ID

这个好处,应该不用过多赘述了。

4. 额外的经验

除了上面这些基本的命名规范,还有一些实用的经验:

  1. 逻辑删除用时间字段 - 用 deleted_atis_deleted 更好,不仅能看出来删除时间,还能用于数据恢复和历史追踪

  2. 状态字段用枚举 - 不要用数字 1、2、3 表示状态,用 status 字段,值用 'pending''approved''rejected' 这样的英文单词

  3. 金额字段用 decimal - 不要用 floatdouble,用 decimal(10,2) 这样的类型,避免浮点数精度问题

  4. 密码字段要加密 - 密码字段名用 password_hashencrypted_password,不要直接叫 password

表结构设计:关系清晰、适度冗余

1. 一对多关系:外键放在多的一边

让我们用用户和订单的业务关系来举例:

用户表 (users)

字段名 类型 说明
id BIGINT 主键
username VARCHAR(50) 用户名
email VARCHAR(100) 邮箱
created_at TIMESTAMP 创建时间

订单表 (orders)

字段名 类型 说明
id BIGINT 主键
user_id BIGINT 用户ID(外键)
order_no VARCHAR(32) 订单号
total_amount DECIMAL(10,2) 总金额
status VARCHAR(20) 订单状态
created_at TIMESTAMP 创建时间

这样设计的好处:

  • 通过 user_id 就知道订单属于哪个用户
  • JOIN 一下就能拿到用户的所有订单
  • 新增订单字段不影响用户表

2. 多对多关系:中间表命名要体现关系

多对多关系的中间表命名要根据具体情况来选择:

情况1:有业务含义的关系表 比如学生和课程的关系,不只是简单关联,还有报名时间、状态等业务信息:

❌ 不好的设计

bash 复制代码
student_course_rel    # rel是什么关系?
sc_mapping           # 缩写看不懂
middle_table         # 完全不知道什么意思

✅ 好的设计

课程报名表 (course_enrollments)

字段名 类型 说明
id BIGINT 主键
student_id BIGINT 学生ID
course_id BIGINT 课程ID
enrolled_at TIMESTAMP 报名时间
status VARCHAR(20) 报名状态

情况2:纯粹的关联关系表 如果只是单纯的多对多映射,没有额外的业务属性,用mapping也是可以的:

用户角色关联表 (user_role_mappings)

字段名 类型 说明
user_id BIGINT 用户ID
role_id BIGINT 角色ID

文章标签关联表 (article_tag_relations)

字段名 类型 说明
article_id BIGINT 文章ID
tag_id BIGINT 标签ID

如何选择命名?

  • 有业务含义的关系:用具体的业务名词,如enrollmentsordersfriendships
  • 纯粹的映射关系:可以用mappingsrelations或直接用实体1_实体2s
  • 关键是保持团队内命名风格的统一

3. 适当的字段冗余:提升查询效率

有时候为了避免复杂的JOIN查询,适当冗余是非常有必要的。

最典型的就是冗余上级ID:

订单详情表 (order_items)

字段名 类型 说明
id BIGINT 主键
order_id BIGINT 订单ID
user_id BIGINT 用户ID(冗余)
product_id BIGINT 商品ID
quantity INT 购买数量
price DECIMAL(10,2) 商品单价

为什么要冗余 user_id?

  • 查询用户的所有购买记录时,直接查 order_items 表就行
  • 不需要先通过 orders 表再关联到 order_items
  • 一个查询代替了两表JOIN

商品评论表 (product_reviews)

字段名 类型 说明
id BIGINT 主键
product_id BIGINT 商品ID
category_id BIGINT 商品分类ID(冗余)
user_id BIGINT 用户ID
rating TINYINT 评分
content TEXT 评论内容

为什么要冗余 category_id?

  • 按分类统计评分时,不需要JOIN商品表
  • 查询某分类下的所有评论更高效

什么时候该冗余ID?

  • 经常需要跨层级查询的场景
  • 统计和报表查询频繁的字段
  • 读多写少的关联关系
  • 上级ID基本不会变动的情况

回到最初的案例

现在我们用上文讲的一些原则来重新设计开头的表:

❌ 原来的设计

bash 复制代码
DC_COURSE_TESTPAPER          # 什么鬼?
DC_COURSE_PAPER_HISTORY      # HISTORY是历史?
DC_COURSE_PAPER_HISTORY_ALL  # ALL又是什么意思?

✅ 重新设计

课程问卷表 (course_questionnaires)

字段名 类型 说明
id BIGINT 主键
course_id BIGINT 课程ID
title VARCHAR(200) 问卷标题
description TEXT 问卷描述
status ENUM 状态:draft/published/closed
created_at TIMESTAMP 创建时间

问卷分发记录表 (questionnaire_assignments)

字段名 类型 说明
id BIGINT 主键
questionnaire_id BIGINT 问卷ID
student_id BIGINT 学生ID
assigned_at TIMESTAMP 分发时间
status ENUM 状态:assigned/started/completed

问卷题目表 (questionnaire_questions)

字段名 类型 说明
id BIGINT 主键
questionnaire_id BIGINT 问卷ID
content TEXT 题目内容
question_type ENUM 题目类型
sort_order INT 排序

现在再看:

  • course_questionnaires - 一眼就知道是课程问卷
  • questionnaire_assignments - 问卷分发记录
  • questionnaire_questions - 问卷题目

是不是瞬间清晰了?

当然,因为业务简单,这里就不统一加前缀了。

写在最后

数据库表结构是项目的重中之重。

好的表设计让能让整个团队开发起来更顺畅,减少沟通成本,而且代码维护起来更容易,新人上手也更简单。

投入时间做好表结构设计,绝对是值得的投资。

没有人愿意每天面对DC_COURSE_PAPER_HISTORY_ALL这样的表名写代码。

最后一句话:代码是写给人看的,表结构也是建给人用的。

相关推荐
一只叫煤球的猫5 小时前
写代码很6,面试秒变菜鸟?不卖课,面试官视角走心探讨
前端·后端·面试
bobz9655 小时前
tcp/ip 中的多路复用
后端
bobz9656 小时前
tls ingress 简单记录
后端
你的人类朋友7 小时前
什么是OpenSSL
后端·安全·程序员
bobz9657 小时前
mcp 直接操作浏览器
后端
程序新视界8 小时前
MySQL中什么是回表查询,如何避免和优化?
mysql
前端小张同学9 小时前
服务器部署 gitlab 占用空间太大怎么办,优化思路。
后端
databook10 小时前
Manim实现闪光轨迹特效
后端·python·动效
武子康10 小时前
大数据-98 Spark 从 DStream 到 Structured Streaming:Spark 实时计算的演进
大数据·后端·spark
该用户已不存在10 小时前
6个值得收藏的.NET ORM 框架
前端·后端·.net