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

Avoid circular reference when two columns in one table point to primary key in another table

我有两个 table。第一个是 Users table,第二个是 Referrals table。推荐 table 看起来像这样:

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

要求是如果B (ID: 2) 被A (ID: 1) 推荐,我们不应该能够反过来插入。换句话说,table 不应该有这样的数据:

| 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 无法实现,但可以通过某些查询在数据库级别实现,那也没关系。但是,+1 用于 django 解决方案。 :)

谢谢。

您不能创建引用其他行的 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 约束确保相同的两个数字不会出现在另一行中,这将防止同一对人出现第二个时间.

您还需要 table 中的一列来指定两个人中的哪一个是推荐人,哪个是推荐人。


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