SQL 中的参照完整性完整性检查

Posted

技术标签:

【中文标题】SQL 中的参照完整性完整性检查【英文标题】:Referential completeness integrity check in SQL 【发布时间】:2015-03-31 02:41:50 【问题描述】:

我有 3 张桌子 - usersteamsteam_members。后者是从team(id)user(id) 的多对多映射(外键分别为teamsusers)。是否有任何完整性检查可以添加到我的数据库中,可以断言虽然没有成员的团队是可能的,但没有团队的用户是不可能的?为了澄清,我想在数据库层强制所有用户必须属于至少 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 反向执行相同的操作,确保如果您删除团队成员资格,则作为成员的用户仍然具有其他团队成员资格。它还必须同时锁定usersteam_members

当然,team_members 还需要对 usersteams 进行 FK 约束,但这应该只适用于 m:n 连接表。

如果您不介意必须按照特定顺序小心行事,例如总是在删除旧成员之前添加新成员,您可以使用普通触发器而不是延迟约束触发器。这会在你做错事后立即给你错误,而不是在 COMMIT 时,但会使原本有效的语句的某些顺序变成错误条件。

如果您确实需要良好的并发性,那么您可能已经吃饱了。

【讨论】:

如果您不需要team_members,那么您是否可以使用数组列来替换team_members、一些触发器来伪造FK,以及确保数组不是空的?比赛条件? @muistooshort 你可以,但是 FK 触发器有一些特殊的技巧,你不能轻易地用用户定义的触发器来模拟。正如您所怀疑的,除非您使用表级锁,否则竞争将是一个问题。在这种情况下,您不妨进行传统的关系建模。 这排除了Mysql,因为它没有延迟约束 @muistooshort:有人曾经为 postgres 写了一个“外键数组”插件:blog.2ndquadrant.com/… @wrick 这实际上是 PostgreSQL 核心的补丁,你不能用插件做到这一点。它有一些主要性能问题,未被 PostgreSQL 9.3 接受。

以上是关于SQL 中的参照完整性完整性检查的主要内容,如果未能解决你的问题,请参考以下文章

SQL Server中的六种约束:主键约束,外键约束,唯一约束,非空约束,检查约束,默认约束

SQL中的外部键约束有啥用?

访问 SQL 以创建一对多关系而不强制参照完整性

SQL Server中常用的SQL语句

使用触发器实现参照完整性操作 (SQL Server)

SQL Server中常用的SQL语句(转):