SQL 中的参照完整性完整性检查
Posted
技术标签:
【中文标题】SQL 中的参照完整性完整性检查【英文标题】:Referential completeness integrity check in SQL 【发布时间】:2015-03-31 02:41:50 【问题描述】:我有 3 张桌子 - users
、teams
和 team_members
。后者是从team(id)
到user(id)
的多对多映射(外键分别为teams
和users
)。是否有任何完整性检查可以添加到我的数据库中,可以断言虽然没有成员的团队是可能的,但没有团队的用户是不可能的?为了澄清,我想在数据库层强制所有用户必须属于至少 1 个团队(而没有要求所有团队必须有 1 个用户)。在 mysql 或 Postgres 中有效的答案是可以接受的。
【问题讨论】:
您使用的是什么数据库? MySQL 还是 Postgres? 我愿意。 只是一个想法。您的约束意味着当您尝试将INSERT
一行转换为users
时,您还必须将INSERT
至少一行转换为team_members
。因此,整个操作不是原子的,必须推迟约束检查,直到两个表都被更改。它闻起来像一些花哨的触发器。有兴趣看到解决方案。
在这个相关答案中解决了同样的问题:***.com/questions/8394177/… 和:***.com/questions/24813000/…(可能重复)
@wrick:我是怎么找到这些的?好吧,我写了它们,所以我知道如何找到它们。搜索[postgres] [referential-integrity] foreign key deferrable
是一个好的开始...
【参考方案1】:
(答案假定 PostgreSQL;如果您使用其他 RDBMS,触发器和锁定的详细信息会有所不同,但大多数 RDBMS 应该能够支持相同的底层操作。)
虽然可以将外键从 users
添加到 teams
,但这样做需要复制知识 - 除了现有的 m:n 关系之外,您基本上还要创建一个额外的 m:1 关系。这是不可取的。
如果您不需要太多并发,这里最好的选择可能是使用延迟约束触发器和表锁定。添加:
users
上的延迟约束触发器 ON INSERT OR UPDATE ... FOR EACH ROW
触发器执行 LOCK TABLE users, team_members IN EXCLUSIVE MODE
,然后检查创建的用户(如果尚未删除)是否至少有一个团队使用通过team_members
。您的应用程序在写入之前也需要LOCK TABLE users IN EXCLUSIVE MODE
,以防止死锁。请注意,EXCLUSIVE MODE
不会阻止 SELECT
。
team_members
上的延迟约束触发器 ON UPDATE OR DELETE ... FOR EACH ROW
反向执行相同的操作,确保如果您删除团队成员资格,则作为成员的用户仍然具有其他团队成员资格。它还必须同时锁定users
和team_members
。
当然,team_members
还需要对 users
和 teams
进行 FK 约束,但这应该只适用于 m:n 连接表。
如果您不介意必须按照特定顺序小心行事,例如总是在删除旧成员之前添加新成员,您可以使用普通触发器而不是延迟约束触发器。这会在你做错事后立即给你错误,而不是在 COMMIT 时,但会使原本有效的语句的某些顺序变成错误条件。
如果您确实需要良好的并发性,那么您可能已经吃饱了。
【讨论】:
如果您不需要team_members
,那么您是否可以使用数组列来替换team_members
、一些触发器来伪造FK,以及确保数组不是空的?比赛条件?
@muistooshort 你可以,但是 FK 触发器有一些特殊的技巧,你不能轻易地用用户定义的触发器来模拟。正如您所怀疑的,除非您使用表级锁,否则竞争将是一个问题。在这种情况下,您不妨进行传统的关系建模。
这排除了Mysql,因为它没有延迟约束
@muistooshort:有人曾经为 postgres 写了一个“外键数组”插件:blog.2ndquadrant.com/…
@wrick 这实际上是 PostgreSQL 核心的补丁,你不能用插件做到这一点。它有一些主要性能问题,未被 PostgreSQL 9.3 接受。以上是关于SQL 中的参照完整性完整性检查的主要内容,如果未能解决你的问题,请参考以下文章