MySQL外键约束

1. 导言

上一节详细介绍了MySQL的查询操作,其中有涉及到对多张表的操作。我们不得不想以下问题:

  • 添加employee时,有没有可能将dept_id添加为一个不存在的部门id?
  • 如果错误的删除了部门表中的某一列,而恰恰有某个员工属于该部门,那会出现什么问题,该如何避免?
  • 如果dept表中的did被更改了,那关联岂不是失效了?

2. 外键约束语法

解决上述问题的方式很简单,只需要对employee表添加外键约束即可,在新创建表时添加外键约束语句:

constraint fk_did foreign key(dept_id) references dept(did)

上述语句中:

  • fk_did 为外键约束名称
  • foreign key(dept_id)指定了本表中的dept_id字段为外键约定的关系字段
  • references dept(did)指定了dept_id受dept表中的did字段约束

创建employee表时的语句:

CREATE TABLE `employee` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `name` varchar(60) NOT NULL COMMENT '姓名',
  `age` tinyint(4) DEFAULT NULL COMMENT '年龄',
  `sex` tinyint(2) NOT NULL DEFAULT '1' COMMENT '性别,1男,2女',
  `salary` decimal(10,2) NOT NULL COMMENT '薪资',
  `hire_date` date NOT NULL COMMENT '聘用日期',
  `dept_id` int(11) DEFAULT NULL COMMENT '部门ID',
  PRIMARY KEY (`id`),
  KEY `fk_did` (`dept_id`),
  CONSTRAINT `fk_did` FOREIGN KEY (`dept_id`) REFERENCES `dept` (`did`)
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8;

当给员工指定一个不存在的部门id时:

mysql> insert into employee(name,age,sex,salary,hire_date,dept_id) values("老张","35","1","15000",NOW(),13);
1452 - Cannot add or update a child row: a foreign key constraint fails (`test`.`employee`, CONSTRAINT `fk_did` FOREIGN KEY (`dept_id`) REFERENCES `dept` (`did`))

当删除一个有员工的部门时:

mysql> delete from dept where dept.did = 1;
1451 - Cannot delete or update a parent row: a foreign key constraint fails (`test`.`employee`, CONSTRAINT `fk_did` FOREIGN KEY (`dept_id`) REFERENCES `dept` (`did`))

当视图更新被关联的外键的值时:

mysql> update dept set did = 7 where did = 1;
1451 - Cannot delete or update a parent row: a foreign key constraint fails (`test`.`employee`, CONSTRAINT `fk_did` FOREIGN KEY (`dept_id`) REFERENCES `dept` (`did`))

在本例中,employee引用了dept表中的did字段作为外键。所以dept表相对于employee表为主表(父表),employee表相对于dept表为从表(子表)。

可以得出以下结论:

  1. 默认情况下,如果子表中有匹配的记录时,则不允许对父表中被引用的键进行更新/删除操作。
  2. 子表外键值只能从父表中已有的指定,或为NULL。

3. 注意事项

  • 子表外键的值要么引用自父表,要么为NULL。
  • 父表中被引用的字段必须要保证唯一性,一般为该表的主键,如果引用的是普通字段,最好使用UNIQUE限制一下唯一性。
  • table引擎需要设置为InnoDB。
  • 插入数据时,先在父表中插入数据,再从子表中插入数据。
  • 删除数据时,先删除子表中的数据,再删除父表中的数据。

4. 约束类型

在上面我们得出过一个结论:

默认情况下,如果子表中有匹配的记录时,则不允许对父表中被引用的键进行更新/删除操作。

注意,上面提到了“默认情况下”,也就是说默认的约束类型是不允许对父表中被引用的键进行更新/删除操作(可以对父表中的其他列进行操作),默认的约束类型是可以更改的。MySQL针对update/delete有四种约束类型,分别是:

  • RESTRICT,默认约束类型,不允许对父表中被引用的键进行更新/删除操作。
  • NO ACTION,同RESTRICT,在MySQL中都是立即检查外键约束。
  • CASCADE,在父表上更新/删除记录时,同步删除子表的匹配记录。
  • SET NULL,在父表上更新/删除记录时,将子表上匹配的记录的列设置为NULL。

4.1 SET NULL 约束类型

在外键约束语句中添加以下内容即设置了删除时SET NULL,更新时SET NULL约束类型

ON DELETE SET NULL ON UPDATE SET NULL

完整的外键约束语句如下

CONSTRAINT `fk_did` FOREIGN KEY (`dept_id`) REFERENCES `dept` (`did`) ON DELETE SET NULL ON UPDATE SET NULL

在操作之前先看一下表中有关联的数据:

mysql> select * from employee,dept where employee.dept_id = dept.did;
+----+-----------+-----+-----+----------+------------+---------+-----+-----------+
| id | name      | age | sex | salary   | hire_date  | dept_id | did | dname     |
+----+-----------+-----+-----+----------+------------+---------+-----+-----------+
|  1 | 菜虚鲲 |  20 |   2 | 10000.00 | 2020-01-10 |       1 |   1 | 研发部 |
|  2 | 奥力给 |  30 |   1 | 18000.00 | 2020-01-08 |       1 |   1 | 研发部 |
|  3 | 老八    |  28 |   1 | 7000.00  | 2020-01-07 |       1 |   1 | 研发部 |
|  4 | 小张    |  25 |   1 | 8000.00  | 2020-01-10 |       1 |   1 | 研发部 |
|  5 | 小红    |  20 |   2 | 6000.00  | 2020-01-05 |       2 |   2 | 人事部 |
|  6 | 小丽    |  23 |   2 | 6500.00  | 2020-01-05 |       2 |   2 | 人事部 |
|  7 | 小花    |  21 |   2 | 5500.00  | 2020-01-10 |       2 |   2 | 人事部 |
|  8 | 马小跳 |  25 |   1 | 7000.00  | 2020-01-01 |       3 |   3 | 测试部 |
|  9 | 张大骚 |  30 |   1 | 9000.00  | 2020-01-07 |       3 |   3 | 测试部 |
| 10 | 马冬梅 |  31 |   2 | 5000.00  | 2020-01-07 |       4 |   4 | 销售部 |
+----+-----------+-----+-----+----------+------------+---------+-----+-----------+
10 rows in set (0.00 sec)

可以明确的看到马小跳和张大骚两列引用的外键id为3,更新dept表,将did = 3 的行更改为did = 7

mysql> update dept set did = 7 where did = 3;
Query OK, 1 row affected (0.01 sec)
Rows matched: 1  Changed: 1  Warnings: 0

看一下表中的数据:

mysql> select * from employee left join dept on employee.dept_id = dept.did;
+----+-----------+-----+-----+----------+------------+---------+------+-----------+
| id | name      | age | sex | salary   | hire_date  | dept_id | did  | dname     |
+----+-----------+-----+-----+----------+------------+---------+------+-----------+
|  1 | 菜虚鲲 |  20 |   2 | 10000.00 | 2020-01-10 |       1 |    1 | 研发部 |
|  2 | 奥力给 |  30 |   1 | 18000.00 | 2020-01-08 |       1 |    1 | 研发部 |
|  3 | 老八    |  28 |   1 | 7000.00  | 2020-01-07 |       1 |    1 | 研发部 |
|  4 | 小张    |  25 |   1 | 8000.00  | 2020-01-10 |       1 |    1 | 研发部 |
|  5 | 小红    |  20 |   2 | 6000.00  | 2020-01-05 |       2 |    2 | 人事部 |
|  6 | 小丽    |  23 |   2 | 6500.00  | 2020-01-05 |       2 |    2 | 人事部 |
|  7 | 小花    |  21 |   2 | 5500.00  | 2020-01-10 |       2 |    2 | 人事部 |
| 10 | 马冬梅 |  31 |   2 | 5000.00  | 2020-01-07 |       4 |    4 | 销售部 |
|  8 | 马小跳 |  25 |   1 | 7000.00  | 2020-01-01 | NULL    | NULL | NULL      |
|  9 | 张大骚 |  30 |   1 | 9000.00  | 2020-01-07 | NULL    | NULL | NULL      |
| 11 | 川坚果 |  60 |   1 | 100.00   | 2020-01-08 | NULL    | NULL | NULL      |
+----+-----------+-----+-----+----------+------------+---------+------+-----------+
11 rows in set (0.00 sec)

可以看到马小跳和张大骚里的dept_id已经变成了NULL。

由此得出结论:

如果在外键上设置了ON UPDATE SET NULL属性后,在父表上更新记录时,将子表上匹配的记录的列设置为NULL。

ON DELETE SET NULL效果也一样,区别就是在删除时设置为NULL,这里就不展示了。

4.2 CASCADE约束类型

使用CASCADE时完整的外键约束语句如下:

CONSTRAINT `fk_did` FOREIGN KEY (`dept_id`) REFERENCES `dept` (`did`) ON DELETE CASCADE ON UPDATE CASCADE

在操作之前先看一下当前的关联数据:

mysql> select * from employee,dept where employee.dept_id = dept.did;
+----+-----------+-----+-----+----------+------------+---------+-----+-----------+
| id | name      | age | sex | salary   | hire_date  | dept_id | did | dname     |
+----+-----------+-----+-----+----------+------------+---------+-----+-----------+
|  1 | 菜虚鲲 |  20 |   2 | 10000.00 | 2020-01-10 |       1 |   1 | 研发部 |
|  2 | 奥力给 |  30 |   1 | 18000.00 | 2020-01-08 |       1 |   1 | 研发部 |
|  3 | 老八    |  28 |   1 | 7000.00  | 2020-01-07 |       1 |   1 | 研发部 |
|  4 | 小张    |  25 |   1 | 8000.00  | 2020-01-10 |       1 |   1 | 研发部 |
|  5 | 小红    |  20 |   2 | 6000.00  | 2020-01-05 |       2 |   2 | 人事部 |
|  6 | 小丽    |  23 |   2 | 6500.00  | 2020-01-05 |       2 |   2 | 人事部 |
|  7 | 小花    |  21 |   2 | 5500.00  | 2020-01-10 |       2 |   2 | 人事部 |
| 10 | 马冬梅 |  31 |   2 | 5000.00  | 2020-01-07 |       4 |   4 | 销售部 |
+----+-----------+-----+-----+----------+------------+---------+-----+-----------+
8 rows in set (0.00 sec)

这次以小红、小丽、小花所在did=2的部门为例,将dept表中did = 2的列的did修改为8:

mysql> update dept set did = 8 where did = 2; 
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

再看一下表中的关联数据:

mysql> select * from employee,dept where employee.dept_id = dept.did;
+----+-----------+-----+-----+----------+------------+---------+-----+-----------+
| id | name      | age | sex | salary   | hire_date  | dept_id | did | dname     |
+----+-----------+-----+-----+----------+------------+---------+-----+-----------+
|  1 | 菜虚鲲 |  20 |   2 | 10000.00 | 2020-01-10 |       1 |   1 | 研发部 |
|  2 | 奥力给 |  30 |   1 | 18000.00 | 2020-01-08 |       1 |   1 | 研发部 |
|  3 | 老八    |  28 |   1 | 7000.00  | 2020-01-07 |       1 |   1 | 研发部 |
|  4 | 小张    |  25 |   1 | 8000.00  | 2020-01-10 |       1 |   1 | 研发部 |
|  5 | 小红    |  20 |   2 | 6000.00  | 2020-01-05 |       8 |   8 | 人事部 |
|  6 | 小丽    |  23 |   2 | 6500.00  | 2020-01-05 |       8 |   8 | 人事部 |
|  7 | 小花    |  21 |   2 | 5500.00  | 2020-01-10 |       8 |   8 | 人事部 |
| 10 | 马冬梅 |  31 |   2 | 5000.00  | 2020-01-07 |       4 |   4 | 销售部 |
+----+-----------+-----+-----+----------+------------+---------+-----+-----------+
8 rows in set (0.00 sec)

可以看到同步修改成了8。
那,现在删除一下dept表中did = 8 的列试一试:

mysql> delete from dept where did = 8;
Query OK, 1 row affected (0.02 sec)

再看看employee中中是否还有小红、小丽、小花这三列:

mysql> select * from employee;
+----+-----------+-----+-----+----------+------------+---------+
| id | name      | age | sex | salary   | hire_date  | dept_id |
+----+-----------+-----+-----+----------+------------+---------+
|  1 | 菜虚鲲 |  20 |   2 | 10000.00 | 2020-01-10 |       1 |
|  2 | 奥力给 |  30 |   1 | 18000.00 | 2020-01-08 |       1 |
|  3 | 老八    |  28 |   1 | 7000.00  | 2020-01-07 |       1 |
|  4 | 小张    |  25 |   1 | 8000.00  | 2020-01-10 |       1 |
|  8 | 马小跳 |  25 |   1 | 7000.00  | 2020-01-01 | NULL    |
|  9 | 张大骚 |  30 |   1 | 9000.00  | 2020-01-07 | NULL    |
| 10 | 马冬梅 |  31 |   2 | 5000.00  | 2020-01-07 |       4 |
| 11 | 川坚果 |  60 |   1 | 100.00   | 2020-01-08 | NULL    |
+----+-----------+-----+-----+----------+------------+---------+

可以很明确的看到,同步删除了。

结论:

  1. 如果在外键上设置了ON UPDATE CASCADE属性后,在父表上更新记录时,子表上匹配的记录也会同步更新。
  2. 如果在外键上设置了ON DELETE CASCADE属性后,在父表上删除记录时,子表上匹配的记录也会同步删除。

注:

  • 最好不要设置成ON DELETE CASCADE约束类型,因为如果误删了父表中的数据,可能会导致数据丢失。推荐采用默认约束类型,如果需要设置删除时的级联关系最好设置为SET NULL约束类型。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: MySQL外键约束是指在一个表中定义的一个列或多个列与另一个表中的一个列或多个列之间的关系。这种关系可以用于保证数据的完整性和一致性,即在更新或删除数据时能够自动处理相关联的数据,避免数据的不一致性和错误。 在 MySQL 中,可以通过以下步骤来创建外键约束: 1. 创建两个表,并在其中一个表中定义一个列或多个列作为外键列,这些列将与另一个表中的列建立关系。 2. 在定义外键列的表中,使用 FOREIGN KEY 关键字来指定外键列,并使用 REFERENCES 关键字来指定另一个表中的列。 3. 在定义外键列的表中,使用 ON DELETE 和 ON UPDATE 子句来定义在删除或更新相关行时如何处理外键约束。可以指定的选项包括 CASCADE、SET NULL、RESTRICT 和 NO ACTION。 例如,以下 SQL 语句用于创建一个名为 "orders" 的表和一个名为 "customers" 的表,并在 "orders" 表中定义一个名为 "customer_id" 的外键列,该列与 "customers" 表中的 "id" 列建立关系: ``` CREATE TABLE customers ( id INT(11) NOT NULL AUTO_INCREMENT, name VARCHAR(50) NOT NULL, PRIMARY KEY (id) ); CREATE TABLE orders ( id INT(11) NOT NULL AUTO_INCREMENT, customer_id INT(11) NOT NULL, order_date DATE NOT NULL, total_price DECIMAL(10,2) NOT NULL, PRIMARY KEY (id), FOREIGN KEY (customer_id) REFERENCES customers(id) ON DELETE CASCADE ON UPDATE CASCADE ); ``` 在上面的例子中,外键约束指定了当在 "customers" 表中删除或更新一行时,对应的 "orders" 表中的行应该怎样处理。由于指定了 ON DELETE CASCADE 和 ON UPDATE CASCADE 选项,当在 "customers" 表中删除或更新一行时,与该行相关的 "orders" 表中的行也将被删除或更新。 ### 回答2: MySQL外键约束是一种用于保持数据完整性和一致性的约束。它定义了表之间的关联关系,确保在一个表中的数据引用另一个表的关联字段时,另一个表中必须存在对应的数据。 外键约束在创建表时通过在一个或多个字段上添加FOREIGN KEY关键字来定义。这些字段通常被称为外键。被引用的表被称为主表或父表,包含外键的表被称为子表或从表。 外键约束可以确保数据的完整性,例如防止在子表中插入无效的外键值。如果尝试在子表中插入一个在主表中不存在的外键值,MySQL会报错并拒绝插入操作。这可以防止引用不存在的数据,避免数据的不一致性。 另外,当主表中的数据被删除或更新时,外键约束还可以实现级联操作。可以定义为当主表中的数据被删除或更新时,子表中对应的数据也会被删除或更新。这样可以确保数据的关联性和一致性。 需要注意的是,使用外键约束会增加数据库的查询和更新操作的开销,因为数据库需要验证外键的正确性。此外,外键约束只能在InnoDB存储引擎下使用,对于其他存储引擎如MyISAM等不支持。 总的来说,MySQL外键约束是一种重要的数据完整性保障机制,可以确保表之间的关联关系和数据的一致性。但同时也需要权衡性能影响,根据具体业务需求进行合理应用。 ### 回答3: MySQL中的外键约束是一种用于确保关系数据库中数据完整性的手段。外键是一个字段或一组字段,用于关联两个表中的数据。 外键约束是通过在被关联表中创建一个外键指向主表的主键来实现的。当在被关联表中插入数据时,数据库会检查该字段的值是否存在于主表中。如果不存在,则会报错并拒绝插入操作。 外键约束有以下几个作用: 1. 数据完整性:外键约束可以确保表之间的关系是有效的,避免了数据的不一致性和冗余。 2. 数据关联:外键约束可以通过关联两个表中的数据,方便进行数据查询和操作。 3. 数据一致性:外键约束可以确保数据在插入、更新或删除时的一致性,避免了数据的不一致性问题。 除了上述作用之外,外键约束还可以实现级联操作,即对主表的一项操作会影响到关联的从表。常见的级联操作有: 1. 级联更新:当主表的主键更新时,关联的从表中的外键值也会相应更新。 2. 级联删除:当主表的主键被删除时,关联的从表中的相关数据也会被删除。 需要注意的是,使用外键约束需要满足以下条件: 1. 被关联表和关联表必须使用InnoDB存储引擎,因为只有InnoDB引擎才支持外键约束。 2. 被关联的字段类型和长度必须与主表的主键字段类型和长度一致。 3. 在创建外键约束之前,必须先创建主表。 总的来说,MySQL中的外键约束是一种简单、有效的数据完整性保证机制,它可以实现数据间的关联、一致性和级联操作。在数据库设计和使用中,合理地运用外键约束可以提高数据的可靠性和一致性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值