外键
外键用于表达表与表之间的引用关系,并由数据库维护引用完整性。
#concept
#growing
#backend
#database
#sql
[!info] related notes
外键
一句话定义
外键是数据库中的引用约束:它要求子表中的引用值必须对应父表中真实存在的候选键,从而维护引用完整性。
核心机制
CREATE TABLE assessments (
id BIGINT PRIMARY KEY,
user_id BIGINT NOT NULL,
FOREIGN KEY (user_id) REFERENCES users(id)
);
这里:
assessments.user_id
↓ references
users.id
如果不存在 users.id = 999,那么插入 user_id = 999 的 Assessment 会被数据库拒绝。
外键的价值不是 JOIN
两个概念职责不同:
FOREIGN KEY
→ 保证持久化关系合法
JOIN
→ 查询时把多张表组合起来
即使没有真正创建外键,也能写 JOIN;反过来,有外键也不代表每次查询都要 JOIN。
应用预检查与数据库最终约束
Service 可以先查询:
User 是否存在?
以便返回友好的业务错误。但只靠“先查再插”存在并发时间窗:检查成功后,父记录可能被另一个事务删除。
因此更稳的分工是:
Application
→ 提前校验、表达业务语义
Database FK
→ 最终守住引用完整性
删除父记录时怎么办
外键还要定义父记录删除后的生命周期策略:
RESTRICT / NO ACTION
仍被引用时拒绝删除父记录。
CASCADE
删除父记录时自动删除引用它的子记录。
SET NULL
保留子记录,但把外键设为 NULL;对应列必须允许为空。
选择哪种策略取决于领域生命周期,而不是哪种写法更省代码。
更新与索引边界
- 外键还可以定义
ON UPDATE行为,但稳定代理主键通常很少修改。 - PostgreSQL 不会因为创建外键就自动为“引用方外键列”创建索引;如果经常按
user_id查询或删除父记录需要检查大量子行,通常应结合访问模式考虑索引。
边界与易混淆点
- 外键不等于“子表主键”;在 1:N 中,同一个
user_id正常情况下会重复多次。 - 外键能表达数据库可见的引用规则,但不能替代“当前登录用户是否有权限操作资源”这类 Authorization。
- 有些系统出于跨库、分片或写入架构原因不启用物理外键,但这意味着引用完整性需要由其他机制承担,而不是关系约束不重要。