当一个表中的两列指向另一个表中的主键时,避免循环引用

Posted

技术标签:

【中文标题】当一个表中的两列指向另一个表中的主键时,避免循环引用【英文标题】:Avoid circular reference when two columns in one table point to primary key in another table 【发布时间】:2020-07-17 11:31:12 【问题描述】:

我有两张桌子。第一个是Users 表,第二个是Referrals 表。引荐表如下所示:

| Referrals table columns | Points to
| ----------------------- | ---
| new_customer_id         | user table's primary key
| referred_by             | user table's primary key

要求是如果 Person B (ID: 2) 由 Person A (ID: 1) 引用,我们应该不能以相反的方式插入。换句话说,表不应该有这样的数据:

| new_customer_id | referred_by |
| --------------- | ----------- |
|       2         |      1      | <-- OK
|       1         |      2      | <-- Both people refering each other should not be allowed

是否可以在数据库级别插入时检查这一点。我正在使用mysql。我读了一些关于 mysql CHECK 约束here 的内容,但不知道如何实现。

我正在使用 django ORM,这是我尝试过但失败的方法。

class Referrals(models.Model):
    customer = models.ForeignKey(get_user_model(), on_delete=models.CASCADE, db_index=True)
    referred_by = models.ForeignKey(get_user_model(), on_delete=models.CASCADE, db_index=True)

    class Meta:
        constraints = [
            models.CheckConstraint(check=models.Q(customer__gt=models.F('referred_by')),
                                   name='referrer_should_come_before_referee')
        ]

如果 Django ORM 无法实现,但在数据库级别可以通过某些查询实现,那也没关系。但是,对于 django 解决方案 +1。 :)

谢谢。

【问题讨论】:

【参考方案1】:

您不能创建引用其他行的 CHECK 约束。它必须在一行内进行评估。

但您可以创建一个 CHECK 约束,要求较小的 id 值位于第一列。

mysql> create table referrals ( 
 referral1 int, 
 referral2 int, 
 check (referral1 < referral2)
);
Query OK, 0 rows affected (0.02 sec)

mysql> insert into referrals values (1,2);
Query OK, 1 row affected (0.01 sec)

mysql> insert into referrals values (2,1);
ERROR 3819 (HY000): Check constraint 'referrals_chk_1' is violated.

然后,您可以通过在这两列上放置 UNQIUE KEY 来防止两个人之间进行多次推荐。

alter table referrals add unique key (referral1, referral2);

如果 CHECK 约束要求第一个数字较小,而 UNIQUE KEY 约束确保相同的两个数字不会出现在另一行,这将防止同一对人再次出现。

您还希望表格中的一列指定两个人中的哪一个是被推荐人,哪个是被推荐人。


请注意,仅 MySQL 8.0.16 及更高版本支持 CHECK 约束。如果您使用的是早期版本的 MySQL,则可以是 simulated with a trigger。

【讨论】:

谢谢。在我升级到 mysql 8.0.19 后它已修复

以上是关于当一个表中的两列指向另一个表中的主键时,避免循环引用的主要内容,如果未能解决你的问题,请参考以下文章

MySQL-其它整理

持久层框架--hibernate

两个表,一个表中的两列关联另一个表的id,如何将这个表中的两列显示为另一个表id对应的内容

可以使用实体框架在另一个表中调用两次作为外键的主键吗?

oracle 唯一索引,唯一约束,主键之间的联系

SQLite 中的主键是不是需要索引?