mysql 外键_MySQL外键相关的扩展

外键:

在实际开发的项目中,一个健壮数据库中的数据一定有很好的参照完整性。如:有学生档案和成绩单两张表,如果成绩单中有张三的成绩,学生档案中张三的档案却被删除了,这样就会产生垃圾数据或者错误数据。为了保证数据的完整性,将两表之间的数据建立关系,因此就需要在成绩表中添加外键约束。

什么是外键:

外键是指引用另一个表中的一列或多列,被引用的列应该具有主键约束或唯一性约束。外键用于建立和加强两个表数据之间的连接。

create database chapter05 charset="utf8";

use chapter05;

create table grade(

id int(4) not null PRIMARY KEY auto_increment,

name varchar(36)

);

create table student(

  sid int(4) not null PRIMARY KEY auto_increment,

  sname varchar(36),

  gid int(4) not null

);

首先创建两个表:一个班级表(grade)和一个学生表(student)。

b542efa8373a6e314797f15139021bad.png

学生表(student)中的gid是学生所在的班级id,是引入了班级表(grade)中的主键id,那么gid就可以作为表student的外键。被引用的表,即表grade是主表,引用外键的表,即表student是从表,两个表是主从关系。表student用gid可以连接表grade中的信息,从而建立了两个表数据之间的连接。

引入外键后,外键列只能插入参照列存在的值。参照列被参照的值不能被删除,这就保证了数据的参照完整性。

为表添加外键约束:

ca453a63377b037fa18e407ab65f324d.png

grade表中的id为主键,student表中的gid为外键。但是结果中不能明确地看出两个表之间的关系。可以使用下面的方式进行查看:

84ab4288ee8764bded1aa3572cee2e44.png

从查询结果可以看出:gid为student表的外键,并且gid外键依赖于grade表中的id主键,这样两个表就通过外键关联起来了。

在为表添加外键约束时,有些需要注意的地方:

1、建立外键的表必须是Innodb型,不能是临时表。因为在msql中只有innodb类型的表才支持外键。

2、定义外键名时,不能加引号,如constraint‘fk_id’或constraint “fk_id”都是错误的。

3、添加外键的时候,也可以在创建表的时候进行添加。

d259556d00fb7c5a6126b7e1c463fa77.png

删除外键约束:

fc94ef67ffb0a583288ad5bd9faf7130.png86dbf921e1909cf125bba7cffe3914ba.png

从查询结果可以看出:表student中的外键约束已经被成功删除。

多学一招:添加外键约束的参数说明。

我们知道了建立外键是为了保证数据的完整和统一性,但如果主表中的数据被删除或修改,从表中对应的数据该怎么办?很明显,从表中对应的数据也应该被删除,否则数据库中会存在很多无意义的垃圾数据。mysql可以在建立外键时添加on delete或on update子句来告诉数据库,怎么样避免垃圾数据的产生。

alter table表名 add constraint fk_id foreign key (外键字段名) references 外表表名(主键字段名);

[on delete {cascade | set null | no action | restrict}]

[on update {cascade | set null | no action | restrict}]

cascade:删除包含与已删除键值有参照关系的所有记录。

set null:修改包含与已删除键值有参照关系的所有记录,使用null值替换(不能用于已标记为not null的字段)

no action:不进行任何操作

restrict:拒绝主表删除或修改外键关联列。(在不定义on delete和on update子句时,这是默认设置,也是最安全的设置)

存储引擎只能是InnoDB,因为只有这种存储模式才支持外键。
外键约束条件有以下4种:

no action方式:如果子表中有匹配的记录,则不允许对父表对应候选键进行update/delete操作 这个是ANSI SQL-92标准,从mysql4.0.8开始支持;

         --无参照完整性关系,有了也不生效。

restrict方式:同no action,都是立即检查外键约束;

- - 限制,指的是如果子表引用父表的某个字段的值,那么不允许直接删除父表的该值。

set null方式:在父表上update/delete记录时,将子表上匹配记录的列设为null 要注意子表的外键列不能为not null。  On delete set null从mysql3.23.50开始可用;,on update set null从mysql4.0.8开始可用。

cascade方式:在父表上update/delete记录时,同步update/delete子表的匹配记录; On delete cascade从mysql3.23.50开始可用,on update cascade从mysql4.0.8开始可用 ;

         -- 级联,删除/更新父表的某条记录,子表中引用该值的记录会自动被删除/更新。

外键约束(On Delete和On Update)都有Restrict,No Action, Cascade,Set Null属性。
外键约束1--ON DELETE

3f7967fcea5d046ad75622edcf2fe15a.png

外键约束2--ON UPDATE

6244db5b9e0a65bde9a285f87a5fec59.png

注:NO ACTION和RESTRICT的区别:只有在及个别的情况下会导致区别,前者是在其他约束的动作之后执行,后者具有最高的优先权执行。

案例演示:

4e9e22155844e0abf2470d144306da97.png

1、级联方式cascade
首先:创建表,并插入两条记录:

create table t_group(id int auto_increment primary key, name varchar(20))engine=InnoDB;

insert into t_group values (1,'Group_1'),(2,'Group_2');

d62ae5f9e56d43341ad6b8b03a5a5d66.png

创建:级联子表:

create table t_user(id int not null primary key, name varchar(10), groupid int, foreign key(groupid) references t_group(id) on delete cascade on update cascade)engine=InnoDB;

183a05b9e33fcb91b1a20b127cca53c1.png

insert into t_user values (1,'dayu',1),(2,'duoduo',2);

7cabf855e55bf38d7aab7644078290ab.png

实体完整性测试,不能插入重复主键值

282d7ed127bad83ab4b5ba110462aa8d.png

参照完整性测试,不能插入在主表中不存在的外键值

b9f12ca9d9f05df210aef112a0e1c9e0.png

级联测试:

7ed90f49f6f26b7eb7250b546b15760d.png

4583538fdf48d391febbc682fe2934d5.png

主表中更新一个记录:

c2a049e60a929d2af2ed88a882eb3d9f.png05ac4229e7ce3c2602f4c55edb82ef63.png

fe15c86e6c16a91302350014bd3a1ddc.png

总结:改变主表数据,从表数据随着更新。

主表中删除一个记录:

ea39297289370f1cf25294dd2a4e5d1f.png

总结:子表中对应的记录自动被删除。

注意:删除表的时候,要先删除从表,再来删除主表。

227319af0f9f7cfb65a2d8bcfbfc85b0.png

2、置空模式(set null,主表记录被删除,从表中对应的值设置为NULL)
首先:创建表,并插入两条记录:

create table t_group(id int auto_increment primary key, name varchar(20))engine=InnoDB;

insert into t_group values (1,'Group_1'),(2,'Group_2');

d62ae5f9e56d43341ad6b8b03a5a5d66.png

创建:置空子表:

create table t_user_1(id int not null primary key, name char(10),groupid int, foreign key(groupid) references t_group(id) on delete set null on update set null)engine=InnoDB;

e9db9bd010570901360262cb00c8cd6a.png

insert into t_user_1 values (1,'dayu',1),(2,'duoduo',2),(3,'huanhuan',2),(4,'maiqi',1);

d947fed13e17cd0c3769a93ba3867778.pngccb27cefc7b48d19a1b730733652a6b0.png

置空测试:
主表中更新一个记录:

235b60f46d099bb965068c1b415eb250.png959c44210f6ffd4589a10daf1c613624.png8009cbda80a29bc334c12bb3d8c75f3c.png

总结:子表中对应的属性值被自动设置为NULL。

主表中删除一个记录:

926931fa55f1ed7c21d4d8351fd14b39.pnga45791ee3de00571e1c85585933bd4f4.png8b7a3584f2b175c05ac523d415d95062.png

总结:子表中对应的属性值被自动设置为NULL。

注意:删除表的时候,要先删除从表,再来删除主表。

6f771b41566ec183700ecec1d10d150d.png

3、禁止模式(no action/restrict),如果在子表中有引用,则不允许在主表中进行更新或删除
首先:创建表,并插入两条记录:

create table t_group(id int auto_increment primary key, name varchar(20))engine=InnoDB;

insert into t_group values (1,'Group_1'),(2,'Group_2');

d62ae5f9e56d43341ad6b8b03a5a5d66.png

创建:禁止模式子表:

create table t_user_2(id int not null primary key, name char(10),groupid int, foreign key(groupid) references t_group(id) on delete no action on update restrict)engine=InnoDB;

f7fac0f5d291c06968674fa9da8f9fc0.png

insert into t_user_2 values (1,'dayu',1),(2,'duoduo',2),(3,'huanhuan',2),(4,'maiqi',1);

248b2aa3915eb8cc18c2aaafed5b0995.png

b736fe38bbb0406993e0acf3ba273570.png

禁止模式测试:
主表中更新一个记录:

5e3688de6c90df25b9a515838935bd12.png

主表中删除一个记录:

374b916f5bf312a806558beb44a6c0d4.png

总结:如果在子表中有引用,则不允许在主表中进行更新或删除。
注意:删除表的时候,要先删除从表,再来删除主表。

0f4ca395d2654025c04909d2eed3cc93.png

总结:主从表的删除

如果存在引用的从表,则主要不能随意删除:

mysql> drop table t_group;

ERROR 1217 (23000): Cannot delete or update a parent row: a foreign key constraint fails

删除从表没有限制:

mysql> drop table t_user_2;

Query OK, 0 rows affected (0.01 sec)

经过以上测试,了解到MySQL通过外键约束实现了数据的完整性与一致性。

操作关联表:

关联关系:

在实际开发中,需要根据实体的内容设计数据表,实体间会有各种关联关系。所以,根据实体设计的数据表之间也存在着各种关联关系。

多对一:

多对一是数据表中最常见的一种关系。如:员工与部门之间的关系,一个部门可以有多个员工,而一个员工不能属于多个部门,也就是说部门表中的一行在员工表中可以有许多匹配行,但员工表中的一行在部门表中只能有一个匹配行。

表之间的关系是通过外键建立的,在多对一的表关系中,应该将外键键在多的一方,否则会造成数据的冗余。

多对多:

多对多也是数据表中的一种关系。如:学生与课程之间的关系,一个学生可以选择多门课程,当然一门课程也供多个学生选择,也就是说学生表中的一行在课程表中可以有许多匹配行,课程表中的一行在学生表中也有许多匹配行。

通常情况下,为了实现这种关系需要定义一张中间表(称为连接表),该表会存在两个外键,分别参照课程表和学生表。在多对多的关系中,需要注意的是,连接表的两个外键都是可以重复的,但是两个外键之间的关系是不能重复的,所以这两个外键又是连接表的联合主键。

一对一:

一对一关系在实际生活中比较常见,如:人与身份证之间就是一对一的有关系,一个人对应一张身份证,一张身份证只能匹配一个人。那么,一对一关系的两张表如何建立外键?

首先,要分清主从关系。从表需要主表的存在才有意义,身份证需要人的存在才会意义。因此人为主表,身份证为从表。要在身份证表中建立外键。由实际经验可知,身份证中的外键必须是非空唯一的,因此,通常会直接用从表(表身份证)中的主键作为外键。

需要注意的是:这种关系在数据库中并不常见,因为以这种方式存储的信息通常会放在一个表中。在实际开发中,一对一关联关系可以应用于以下几方面:

1.分割具有很多列的表。

2.由于安全原因而隔离表的一部分。

3.保存临时的数据,并且可以毫不费力地通过删除该表而删除这些数据。

添加数据:

在实际开发中,最常见的关联关系就是多对一关系。

在student表和grade表中添加外键约束来建立两个表的关联关系:

42163679cde79527377ade258eb66f26.png

从查询结果可以看出:student表的外键约束已经成功添加。此时,表student和表grade之间是多对一的关系。因为,外键列只能插入参照列存在的值,所以,如果要为两个表添加数据,就需要先为主表grade添加数据。

7a731ce118da9f98b6b046a13f086c72.png

添加的主键id为1和2,由于student表的外键与grade表的主键关联,因此,在为student表添加数据时,gid的值只能是1或2,不能使用其他的值。

5643d8a37670a2fd280dd5f7d4076648.png

这样,这两个表之间的数据就具有关联性。假如要查询class001有哪些学生,首先,需要查询class001班的id,然后,再根据这个id在student表中查询该班级有哪些学生。

在grade表中查询出班级名称为class001的id:

17be3f0b265de3ba1fd7e1d9f480bfab.png

在student表中,查询gid=1的学生,即为class001班的学生。

ce9d3f149f83e3a44f9920cd873731bd.png

通过子查询来实现该功能(查询班级为class001的都有哪些学生):

select sname from student where gid in (select id from grade where name=”class001”)

select sname from student where gid = any (select id from grade where name=”class001”)

select sname from student where gid = some (select id from grade where name=”class001”)

删除数据:

如:学校的class001班取消了,就需要在数据库中将该班级以及该班级的学生一起删除。由于grade表和student表之间具有关联关系。参照列被参照的值是不能被删除的,因此,在删除class001班时,一定要先删除该班级的所有学生,然后,再删除班级。

将class001班的所有学生进行全部删除:

eed18350a6be6677241a82554ca06299.png

从上述的结果可以看出:student表中已经没有任何学生的记录了。

在grade表中,将class001班删除:

99e15d387f4f8988e97fdd40a22403ec.png

这样就删除了关联表中的数据。

如果直接删除表grade的class002班,会出现什么情况呢?

7da2fba5adf5bc915ede94a0b9c69b6b.png

由此可以看出:在两个具有关联关系的表中删除数据时,一定要先删除从表中的数据,然后,再删除主表中的数据,否则会报错的。

需要注意的是:在实际情况中,想要删除class001班,并不需要删除class001班的学生,可以将表student中“wanghong”和“liqiang”的gid改成null,只要主表中该列没有被从表参照就可以删除。但是在建表时,gid字段有非空约束,所以,只能将“wanghong”和“liqiang”的记录删除。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值