源表能否定义指向派生主键表的外键?附场景问询
首先直接给结论:从技术实现上看,这么做是能跑通的,但从数据库设计逻辑和你的业务场景来说,这完全不合理,还会给后续的数据操作埋坑。
咱们先掰扯清楚外键的本质:外键的核心是维护参照完整性——它要求「子表的字段值必须存在于父表的主键/唯一键中」。也就是说,子表依赖父表的数据存在。
回到你的场景:你说OriginSurnames是基于Students表派生出来的,用来存Students里不同姓氏的起源。那正常的业务逻辑应该是:先有学生(Students里的记录),才有去维护对应姓氏起源的需求(往OriginSurnames里加数据)。但如果你把Students的StudentSurName设为指向OriginSurnames的外键,逻辑就完全反过来了:
- 你必须先在OriginSurnames里新增一个姓氏,才能往Students里添加这个姓氏的学生
- 要是哪天你想删除OriginSurnames里的某个姓氏,只要Students里还有这个姓氏的学生,数据库就会因为外键约束拦着你删
这显然和你“OriginSurnames的姓氏都来自Students”的初衷背道而驰,操作起来会非常别扭。
那正确的姿势是什么?应该把OriginSurnames的StudentSurName设为外键,指向Students表的StudentSurName——不过这里要注意,Students的StudentSurName得先加个唯一约束(或者把它设为复合主键的一部分?不过更合理的是单独加唯一约束),因为外键引用的必须是能唯一标识的列。
这样设置的话,就能保证:只有当Students里已经存在某个姓氏时,你才能在OriginSurnames里添加这个姓氏的起源信息,完美匹配你的业务场景,同时也能防止有人往OriginSurnames里加Students根本没有的姓氏。
当然,也有例外情况:如果你的业务规则是「所有学生的姓氏必须预先在OriginSurnames里登记过才能录入」(比如学校只允许使用已收录起源的姓氏),那你原来的外键设置才合理,但这和你描述的“OriginSurnames的姓氏来自Students”完全是两种场景,显然不适用你现在的情况。
内容的提问来源于stack exchange,提问作者alvy

