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

亲身经历

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

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

比如说:

复制代码
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这样的表名写代码。

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

相关推荐
胡gh4 小时前
页面卡成PPT?重排重绘惹的祸!依旧性能优化
前端·javascript·面试
ningqw4 小时前
SpringBoot 常用跨域处理方案
java·后端·springboot
你的人类朋友4 小时前
vi编辑器命令常用操作整理(持续更新)
后端
胡gh4 小时前
简单又复杂,难道只能说一个有箭头一个没箭头?这种问题该怎么回答?
javascript·后端·面试
言兴4 小时前
# 深度解析 ECharts:从零到一构建企业级数据可视化看板
前端·javascript·面试
uzong5 小时前
技术人如何对客做好沟通(上篇)
后端
颜如玉6 小时前
Redis scan高位进位加法机制浅析
redis·后端·开源
遇见你的雩风6 小时前
【MySQL】CRUD基础详解
数据库·mysql
Moment6 小时前
毕业一年了,分享一下我的四个开源项目!😊😊😊
前端·后端·开源