外键列/约束消歧
所以我了解如何创建外键,并且知道 FK 的目的是什么。但是我在理解如何使用它们方面存在问题。
假设您指的是外键约束,简短的回答是您只是不使用它们。
长篇大论来了:
我们习惯于将列称为其他表的外键。特别是在规范化过程中,像“是表user_purchase.i_id
的外键items
”这样的短语会很常见。虽然这是描述这种关系的一种完全有效的方式,但当我们到达实施阶段时,它可能会变得有点模糊。
假设您创建了没有子句FOREIGN KEY
的表:
CREATE TABLE user(
id INT(11) NOT NULL AUTO_INCREMENT,
username VARCHAR(50) NOT NULL,
password VARCHAR(20) NOT NULL,
PRIMARY KEY (id)
);
CREATE TABLE items(
i_id INT(11) NOT NULL AUTO_INCREMENT,
name TINYTEXT NOT NULL,
price DECIMAL(8,2) NOT NULL,
PRIMARY KEY (i_id)
);
CREATE TABLE user_purchase(
i_id INT(11) NOT NULL,
name TINYTEXT NOT NULL,
id INT(11) NOT NULL,
);
请注意,在关系方面,外键列仍然被实现。有一列引用user
表 ( id
),另一列引用items
表 ( i_id
) —— 让我们先把name
列放在一边。考虑以下数据:
user user_purchase items
| id username | | id i_id | | i_id name price |
| 23 john | | 55 10 | | 10 chocolate bar 3.42 |
| 55 mary | | 70 10 | | 33 mobile phone 82.11 |
| 70 fred | | 70 33 | | 54 toothpaste 8.67 |
| 55 10 | | 26 toy car 6.00 |
| 70 26 |
关系就在那里。它是通过user_purchase
表来实现的,该表包含有关谁买了什么的信息。如果我们要查询数据库以获取相关报告,我们会这样做:
select * from user_purchase p
join user u on (p.id=u.id)
join items i on (p.i_id=i.i_id)
这就是我们使用关系和所涉及的外键列的方式。
现在,如果我们这样做:
insert into user_purchase (id,i_id) values (23,99)
显然,这是一个无效条目。虽然有一个用户带有id=23
,但没有项目带有i_id=99
。RDBMS 将允许这种情况发生,因为它不知道任何更好的 . 然而。
这就是外键约束发挥作用的地方。通过FOREIGN KEY (i_id) REFERENCES items(i_id)
在user_purchase
表定义中指定,我们实质上为 RDBMS 提供了一个要遵循的规则:列中不包含值的条目是不可接受i_id
items.i_id
的。换句话说,当外键列实现引用时,外键约束强制引用完整性。
但是请注意,上述内容select
不会改变,只是因为您定义了 FK 约束。因此,您不使用 FK 约束,RDBMS 会这样做,以保护您的数据。
冗余
...如果我想要几列怎么办?为什么我们不对同一张表的不同列使用多个外键?
问问自己:你为什么想要那个?如果两个外键用于相同的目的,冗余最终会给你带来麻烦。考虑以下数据:
user_purchase items
| id i_id name | | i_id name price |
| 55 10 chocolate bar | | 10 chocolate bar 3.42 |
| 70 10 chocolate bar | | 33 mobile phone 82.11 |
| 70 33 mobile phone | | 54 toothpaste 8.67 |
| 55 10 toothpaste | | 26 toy car 6.00 |
| 70 26 toy car |
这张照片有什么问题?用户55
是买了两根巧克力棒,还是买了一根巧克力棒和一根牙膏?这种模棱两可会导致需要花费大量精力来保持数据同步,如果我们只保留一个外键,这将是不必要的。事实上,为什么不name
完全删除该列,因为它是由关系隐含的。
当然,我们可以通过实现一个复合外键来解决这个问题,通过PRIMARY KEY(i_id,name)
为items
表设置(或定义一个额外的UNIQUE(i_id,name)
索引,这并不重要),然后设置一个FOREIGN KEY(i_id,name) REFERENCES items(i_id,name)
. items
这样,只有存在于表中的 (i_id,name)对对user_purchases
. 除了您仍然有一个外键这一事实之外,这种方法是完全没有必要的,只要该i_id
列已经足以识别一个项目(不能对name
列说同样的话......)。
但是,没有规则反对对表使用多个外键。事实上,有些情况需要这种方法。考虑一个person(id,name)
表和一个表,parent(person,father,mother)
其中包含以下数据:
person parent
| id name | | person father mother |
| 14 John | | 21 14 59 |
| 43 Jane | | 14 76 43 |
| 21 Mike |
| 76 Frank |
| 59 Mary |
显然,表的所有三列parent
都是person
. 但是,不是针对同一个关系,而是针对三个不同的关系:由于一个人的父母也是人,因此两个对应的列必须引用同一个表person
。但是请注意,这三个字段不仅可以而且必须person
在同一行中引用不同的 s parent
,因为没有人是他自己的父母,而且没有人的父亲也是他的母亲。