Skip to content

064. 中间表、ORM 关系与统计查询

学习目标

这一节把关联查询放回项目业务里,并引出 ORM 关系查询的取舍。

学完后,你应该能理解:

  • 项目成员为什么需要中间表;
  • 中间表和外键不是一回事;
  • 多对多关系如何增删;
  • 项目已花费金额可以如何统计;
  • ORM relationship 为什么像黑盒;
  • 为什么复杂查询最终仍然离不开 SQL。

项目成员关系

项目和用户是多对多关系。

一个项目可以有多个成员。

一个用户也可以参与多个项目。

这种关系通常用中间表保存。

中间表里至少有:

  • 项目 ID;
  • 用户 ID。

这两列共同描述“哪个用户参与了哪个项目”。

中间表命名

课程项目里,中间表会用固定前缀区分。

例如以 rel 开头,表示关系表。

这样看到表名时,就能快速判断它不是业务主表,而是用来描述对象关系的表。

查询项目成员

查询某个项目的成员时,可以从中间表开始。

流程是:

  1. 按项目 ID 查询中间表;
  2. 拿到用户 ID;
  3. 关联用户表;
  4. 返回用户信息。

如果项目新增一个成员,本质上就是往中间表插入一条关系记录。

如果删除成员,本质上就是删除这条关系记录。

中间表可以有额外字段

中间表不一定只有两个 ID。

如果关系本身也有信息,可以增加字段。

例如项目成员关系里可以记录:

  • 角色;
  • 职位;
  • 加入时间;
  • 权限级别。

这时中间表不仅表示“有关系”,还描述“是什么关系”。

中间表不是外键

中间表和外键是两码事。

中间表用于描述两个业务对象之间的关系。

外键用于在数据库层面约束某个字段必须引用另一张表的合法记录。

可以有中间表但不建外键。

也可以没有中间表,但某个字段逻辑上指向另一张表。

为什么不强依赖数据库外键

实际业务项目里,很多团队不会在数据库层面大量使用外键。

原因是外键会让删除、数据迁移、导入、批量修复变复杂。

课程项目把关系约束放在后端业务代码里处理。

这样应用逻辑更可控。

一对多字段关系

不是所有关系都需要中间表。

例如项目表里有 leader_id

它表示这个项目的负责人是谁。

一个项目只有一个负责人。

这种关系可以直接在项目表里保存用户 ID。

查询负责人时,用 leader_id 关联用户表即可。

项目花费金额

项目表里可以存一个已花费金额字段。

但也可以不存。

如果不存,就通过审批单统计出来。

例如项目下有多张审批单,每张审批单有金额和状态。

只统计审批通过的审批单金额,求和后就是项目已花费金额。

这种方式避免了并发更新项目金额字段,但查询会更复杂。

报销单、审批单与项目

项目、报销单、审批单之间有关系。

报销单会进入审批流程。

审批通过后,会产生或更新审批单状态。

审批单里保存项目 ID 和金额。

统计项目花费时,就按项目 ID 聚合审批单金额。

用 SQL 做统计

统计查询适合直接写 SQL。

例如:

  • 按项目 ID 过滤;
  • 只统计审批通过的数据;
  • 对金额做 sum
  • 按项目分组;
  • 返回项目与已花费金额。

SQL 写清楚后,逻辑是透明的。

你能看到每一步 join、where、group by 和聚合。

ORM relationship

SQLModel 这类 ORM 支持定义关系属性。

例如项目模型可以定义负责人关系。

用户模型也可以定义自己负责的项目列表。

查询时可以通过 relationship 预加载相关数据。

这样写起来很方便。

relationship 的黑盒感

ORM 自动生成 SQL。

这带来便利,也带来黑盒感。

当关联层级多、表很多、数据量大时,你可能不知道它到底生成了什么 SQL。

如果 SQL 没走索引,或者查出了过多数据,性能问题会比较难排。

所以复杂查询不能完全依赖 ORM 自动关系。

手写 SQL 的必要性

当项目里有统计、报表、多表聚合、动态查询时,最终还是要能写 SQL。

ORM 可以处理简单 CRUD。

复杂查询更需要你清楚地控制:

  • 查哪些字段;
  • join 哪些表;
  • 用哪些条件;
  • 是否命中索引;
  • 聚合和分组怎么做。

这也是为什么很多后端项目仍然保留大量手写 SQL。

后续封装方向

课程后面会封装一个更适合项目的查询能力。

目标是兼顾:

  • ORM 的便利;
  • 手写 SQL 的可控;
  • 动态查询表达式;
  • 自动生成接口;
  • 热更新配置;
  • 测试保障。

也就是说,不是完全依赖 ORM,也不是每个接口都从零手写。

这一节的重点

这一节把关联查询和业务表关系连接起来。

你需要记住:

  • 多对多通常用中间表;
  • 中间表和外键不是一回事;
  • 项目成员关系就是中间表记录;
  • 项目花费可以通过审批单聚合统计;
  • ORM relationship 方便但有黑盒风险;
  • 复杂统计查询最终还是要懂 SQL。

AI Agent 课程学习文档。