Administrator
Administrator
发布于 2026-10-27 / 0 阅读
0
0

数据库入门到初级(16):一张表设计得好不好,看哪些细节

数据库入门到初级 · 第 16/22 篇。实验口径:MySQL 8.4、InnoDB、utf8mb4。案例:小型商城。

两张表可能都能完成今天的功能,但半年后,一张容易维护,另一张让每次改需求都像拆盲盒。区别往往藏在命名、状态和约束这些细节里。本篇给出一套适合初级开发者使用的检查方法。

1. 名字首先要准确

amount1、time2、flag 看起来省事,却把解释工作推给了后来的人。total_amount、created_at、is_on_sale 更能说明用途。

命名风格应在项目内一致。表名用单数还是复数都可以,重要的是不要混用;字段尽量避免容易与关键字冲突的名称。还要写清金额单位、时间约定和状态值,名称不能替代完整说明。

表设计检查的三个维度

2. 主键与业务编号不要混为一谈

自然键来自业务,例如某些稳定编码;代理键是系统额外分配的标识,例如自增 id。自然键有时简洁,但业务规则可能变化;代理键更容易作为内部关联身份。

真实订单经常同时有内部 id 和对外 order_no。前者用于表关联,后者用于客服查询和外部交流;业务订单号仍需要唯一约束。自增编号不保证连续,也不应该承担防止别人猜测订单的权限功能。

3. 唯一性必须落到数据库

应用先查“邮箱不存在”再插入,遇到并发时,两条请求可能同时查到不存在。数据库唯一约束才是最后一道防线。应用要捕获冲突,并转成用户能够理解的提示。

SHOW INDEX FROM users;
SHOW CREATE TABLE order_items;

检查邮箱是否唯一,也检查订单和商品的组合是否符合你选择的明细粒度。允许什么重复,要以业务为准,不是“凡是看起来重复就加唯一”。

4. 状态字段不只是几个数字

status=1 到底是待支付还是已支付?如果采用数字编码,必须有清楚的数据字典。系列采用 pending、paid、cancelled,更便于直接阅读。

检查约束能限制状态集合,却不能直接保证状态转换顺序。例如已取消订单不能重新支付,需要应用通过带条件的更新控制:

START TRANSACTION;
UPDATE orders
SET status = 'paid'
WHERE id = 1003 AND status = 'pending';
SELECT ROW_COUNT() AS changed_orders;
ROLLBACK;

这里仅演示状态转换的条件控制,不代表已经接入真实支付。生产系统还要验证付款来源、金额、重复通知等事实。

5. 创建时间、更新时间和事件时间

created_at 是记录创建时间,paid_at 是支付完成时间,二者不是一回事。updated_at 只能说明最近一次修改,无法代替完整审计日志。

如果需要追踪“谁在何时把订单从什么状态改为什么状态”,应记录独立事件或审计表。不要寄希望于一列更新时间还原全部历史。

6. 逻辑删除也有成本

使用 deleted_at 标记删除可以保留历史,但所有相关查询都要记得排除已删除数据,唯一约束和关联规则也要重新设计。比如邮箱唯一时,被软删除账号仍可能占用邮箱。

逻辑删除适合某些业务,但不是所有表都应加一个 is_deleted。明确删除意味着什么,以及恢复、保留和清理策略,再决定实现。

7. 不要让字符串承担关系模型的工作

用户拥有多个角色时,不宜直接存 roles='admin,editor' 再到处拆分。角色需要独立查询、约束和关联时,用角色表与关联表更合适。相反,纯展示备注不需要为了“规范”拆成几十张表。

练习与答案

练习:设计“用户昵称唯一”,应用已经先查重,还要唯一约束吗? 要。应用检查改善提示,数据库约束处理并发冲突。

练习:只有 updated_at,能知道用户过去三次改名吗? 不能。要保留独立变更记录。

检查设计时,最有用的提问是:“如果两个人同时操作,或者业务在半年后变化,这个字段的含义还能保持清楚吗?”


上一篇:第 15 篇 · 系列目录 · 下一篇:第 17 篇


评论