创建时间: 2026-09-01最后更新: 2026-09-01

1. 需求

数据建模不是先打开数据库工具创建表,而是先弄清楚系统需要长期保存什么,以及这些数据之间有什么规则。

我们的任务管理系统需要支持这些操作:

  1. 用户创建项目;
  2. 项目可以有多名成员;
  3. 项目中可以创建任务;
  4. 一条任务可以绑定多个标签;
  5. 用户只能查看自己参与的项目;
  6. 任务需要记录状态、优先级和截止时间。

从需求中可以识别出四个核心实体:用户、项目、任务和标签。项目成员、任务标签虽然不是独立存在的业务对象,却需要记录两个实体之间的关系,因此也要建表。这类表通常叫作关联表,关系上的角色、加入时间等属性也保存在这里。

model-overview.txt
1
users (1) ----< projects (N) projects.owner_id
2
users (N) >---< projects (N) project_members
3
projects (1) ----< tasks (N) tasks.project_id
4
tasks (N) >---< tags (N) task_tags

图中的 (1) 和 (N) 表示关系两端的数量。例如,一个项目只有一个所有者,一个用户可以拥有多个项目;一条任务也可以绑定多个标签,一个标签又能用于多条任务。

不要把接口 JSON 直接当成数据库模型。接口为了展示方便,可能把项目、负责人和任务嵌套在一个对象里;数据库需要避免重复保存同一事实,并让关系可以被约束和查询。

2. 实体

先确定每个实体自身的属性:

实体核心字段
用户id、email、display_name、created_at
项目id、owner_id、name、created_at
任务id、project_id、task_number、title、status、priority、时间字段
标签id、name

一个字段应该只表达一个稳定事实。例如把 owner_name 复制到 projects 中,会让用户改名后出现两份不同名称。项目只保存 owner_id,读取时再关联 users。

用户、项目、任务和标签这些核心实体使用没有业务含义的代理键 id。邮箱虽然在当前规则下唯一,但它可能被用户修改,不适合作为其他表长期引用的主键。

关联表不一定需要额外的代理键。project_members 使用 (project_id, user_id) 作为联合主键,task_tags 使用 (task_id, tag_id) 作为联合主键,它们都能直接表达一段关系不能重复出现。

业务唯一性仍然应该通过约束保留:

业务规则已有数据库约束
邮箱在全部用户中唯一users_email_key UNIQUE (email)
任务编号在所属项目中唯一tasks_project_number_key UNIQUE (project_id, task_number)

这两条约束已经在前文创建,无需重复执行 ALTER TABLE。代理主键负责稳定引用,唯一约束负责阻止业务重复,两者职责不同。第二条还是联合唯一约束:不同项目可以使用相同任务编号,同一项目内不能重复。

正在验证登录状态
请稍候,验证完成后将继续显示文章内容