MySQL的外键约束是用来在两个表之间建立链接的,其中一个表发生变化,另外一个表也发生变化。从这个特点来看,它主要是为了保证表数据的一致性和完整性的。
对于两个通过外键关联的表,相关联字段中主键所在的表是主表,也称之为父表,外键所在的表是从表,也称之为子表,定义外键的时候需要遵守几个规则:
1、父表必须已经存在于数据库中,或者是当前正在创建的表。如果是后一种情况,则父表与子表是同一个表,这样的表称为自参照表,这种结构称为自参照。
2、必须为父表定义主键。
3、主键不能包含空值,但允许在外键中出现空值。也就是说,只要外键的每个非空值出现在指定的主键中,这个外键的内容就是正确的。
4、外键中列的数目必须和父表的主键中列的数目相同。
5、外键中列的数据类型必须和父表主键中对应列的数据类型相同。说这么多比较笼统,还是看看例子吧。
mysql:yeyztest ::create table fk_test_1( -> id int not null primary key auto_increment, -> name varchar() default ''); Query OK, rows affected (0.10 sec) mysql:yeyztest ::create table fk_test_2( -> id int not null primary key auto_increment, -> uid int, -> foreign key fk_uid(uid) references fk_test_1(id)); Query OK, rows affected (0.06 sec)
这里我们创建两个表,一个是fk_test_1,一个是fk_test_2,其中fk_test_2的uid列上设置外键,关联fk_test_1的表的id列,这里很明显,fk_test_1是父表,而fk_test_2是子表,接下来我们进行数据插入实验。
mysql:yeyztest ::insert into fk_test_1 values (,'aaa'),(,'bbb'); Query OK, rows affected (0.00 sec) Records: Duplicates: Warnings: mysql:yeyztest ::select * from fk_test_1; +----+------+ | id | name | +----+------+ | | aaa | | | bbb | +----+------+ rows in set (0.00 sec) mysql:yeyztest ::insert into fk_test_2 values (,),(,); Query OK, rows affected (0.00 sec) Records: Duplicates: Warnings: mysql:yeyztest ::insert into fk_test_2 values (,); Query OK, row affected (0.00 sec) mysql:yeyztest ::insert into fk_test_2 values (,); ERROR (): Cannot add or update a child row: a foreign key constraint fails (`yeyztest`.`fk_test_2`, CONSTRAINT `fk_test_2_ibfk_1` FOREIGN KEY (`uid`) REFERENCES `fk_test_1` (`id`))
先在主表上插入两条数据,分别是id=1和id=2的数据,然后再子表插入数据,子表插入uid=1和uid=2的数据都能成功,而要插入uid=3的数据时提示失败,也就是说,默认情况下,子表进行插入时,插入的外键关联字段值必须是父表被关联的列包含的值。注意这里的默认情况,后续会进行说明。
再来看看删除的情况,
mysql:yeyztest ::select * from fk_test_2 ; +----+------+ | id | uid | +----+------+ | | | | | | | | | +----+------+ rows in set (0.00 sec) mysql:yeyztest ::delete from fk_test_2 where id=; Query OK, row affected (0.00 sec) mysql:yeyztest ::select * from fk_test_1 ; +----+------+ | id | name | +----+------+ | | aaa | | | bbb | +----+------+ rows in set (0.00 sec) mysql:yeyztest ::delete from fk_test_1 where id=; ERROR (): Cannot delete or update a parent row: a foreign key constraint fails (`yeyztest`.`fk_test_2`, CONSTRAINT `fk_test_2_ibfk_1` FOREIGN KEY (`uid`) REFERENCES `fk_test_1` (`id`))
可以看到,在子表fk_test_2上进行删除,没有出现任何问题,而在父表fk_test_1上删除时,显示无法删除id=1的值,原因是有一个外键约束存在,也就是说,默认情况下,在父表进行删除时,无法直接删除子表中已经存在依赖关联的列值。注意这里的默认情况,下面将会说明。
既然delete不成功,试试update,
mysql:yeyztest ::update fk_test_1 set id= where id=; ERROR (): Cannot delete or update a parent row: a foreign key constraint fails (`yeyztest`.`fk_test_2`, CONSTRAINT `fk_test_2_ibfk_1` FOREIGN KEY (`uid`) REFERENCES `fk_test_1` (`id`)) mysql:yeyztest ::update fk_test_1 set name='ccc' where id=; Query OK, row affected (0.00 sec) Rows matched: Changed: Warnings:
可以看到,update父表的主键列还是不能成功执行,但是update其他的列,可以成功执行。
到这里,我们已经知道,外键的存在是为了保证数据的完整和统一性,但是也带来了一点问题,那就是父表中凡是被子表依赖的列,都没办法删除了,这不是我们想要的,有一些数据确实会过期,我们有删除的需求,那么这个时候应该怎么办?
在上面的测试中,我们反复提到一个词,就是默认情况,我们没有设置外键的删除和更新规则,这里mysql帮我们使用了最严格的的规则,那就是restrict,其实还有其他一些规则,这里全部列出来:
- delete父表的情况:
cascade,set null,no action,restrict
- update父表的情况:
cascade,set null,no action,restrict
其中
- restrict是默认操作,它表示拒绝父表删除或者修改外键已经被子表所依赖的列,这是最安全的设置;
- cascade表示在父表发生删除的时候直接删除子表的记录,这是最危险的设置;
- set null表示父表删除的时候,对子表进行null值处理;
- no action表示父表删除的时候,子表不进行任何改动。
设置关联的语法如下:
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的情况:
mysql:yeyztest ::select * from fk_test_1; +----+------+ | id | name | +----+------+ | | ccc | | | bbb | +----+------+ rows in set (0.00 sec) mysql:yeyztest ::select * from fk_test_2; +----+------+ | id | uid | +----+------+ | | | | | | +----+------+ rows in set (0.00 sec) mysql:yeyztest ::show create table fk_test_2\G *************************** 1. row *************************** Table: fk_test_2 Create Table: CREATE TABLE `fk_test_2` ( `id` int() NOT NULL AUTO_INCREMENT, `uid` int() DEFAULT NULL, PRIMARY KEY (`id`), KEY `fk_uid` (`uid`), CONSTRAINT `fk_test_2_ibfk_1` FOREIGN KEY (`uid`) REFERENCES `fk_test_1` (`id`) ) ENGINE=InnoDB AUTO_INCREMENT= DEFAULT CHARSET=utf8 row in set (0.00 sec) mysql:yeyztest ::alter table fk_test_2 drop foreign key fk_test_2_ibfk_1; Query OK, rows affected (0.02 sec) Records: Duplicates: Warnings: mysql:yeyztest ::alter table fk_test_2 add constraint fk_uid foreign key (uid) references fk_test_1(id) on delete cascade; Query OK, rows affected (0.03 sec) Records: Duplicates: Warnings: ####################################### ####此处删除父表id=的记录,查看子表的结果### ####################################### mysql:yeyztest ::delete from fk_test_1 where id=; Query OK, row affected (0.00 sec) mysql:yeyztest ::select * from fk_test_1 ; +----+------+ | id | name | +----+------+ | | ccc | +----+------+ row in set (0.00 sec) mysql:yeyztest ::select * from fk_test_2 ; +----+------+ | id | uid | +----+------+ | | | +----+------+ row in set (0.00 sec)
可以看到,一开始,父表的值包含id=1和id=2的值,子表的值包含uid=2和uid=1的值,当我们删除父表的id=2的值之后,子表中uid=2的值也直接被删除了。这就是cascade的作用,也就是级联删除。
在看一眼set null的情况:
mysql:yeyztest ::alter table fk_test_2 drop foreign key fk_uid; Query OK, row affected (0.02 sec) Records: Duplicates: Warnings: mysql:yeyztest ::alter table fk_test_2 add CONSTRAINT `fk_uid` FOREIGN KEY (`uid`) REFERENCES `fk_test_1` (`id`) ON DELETE set null; Query OK, row affected (0.03 sec) Records: Duplicates: Warnings: mysql:yeyztest ::delete from fk_test_1 where id=; Query OK, row affected (0.00 sec) mysql:yeyztest ::select *from fk_test_1 ; Empty set (0.00 sec) mysql:yeyztest ::select *from fk_test_2 ; +----+------+ | id | uid | +----+------+ | | NULL | +----+------+ row in set (0.00 sec)
可以看到,设置了set null之后,当父表删除id=1的值时,子表的uid的值变成了null,而没有删除记录。
no action的情况也是类似,只不过是子表的记录没有发生任何改动。
以上是父表进行delete的操作,当父表进行update的时候,子表可以选择的情况也有以上四种,和delete基本保持一致,这里不再赘述。有兴趣可以自己测试一发。
最后,说明一点,子表的外键列可以为空值。
mysql:yeyztest ::insert into fk_test_1 values (,); Query OK, row affected (0.00 sec) mysql:yeyztest ::select *from fk_test_2 ; +----+------+ | id | uid | +----+------+ | | NULL | +----+------+ row in set (0.00 sec) mysql:yeyztest ::insert into fk_test_2 values (,NULL); Query OK, row affected (0.00 sec) mysql:yeyztest ::insert into fk_test_2 values (,NULL); Query OK, row affected (0.00 sec) mysql:yeyztest ::select * from fk_test_2; +----+------+ | id | uid | +----+------+ | | NULL | | | NULL | | | NULL | +----+------+ rows in set (0.00 sec)
以上就是MySQL外键约束的实例讲解的详细内容,更多关于MySQL外键约束的资料请关注其它相关文章!
免责声明:本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除!
《魔兽世界》大逃杀!60人新游玩模式《强袭风暴》3月21日上线
暴雪近日发布了《魔兽世界》10.2.6 更新内容,新游玩模式《强袭风暴》即将于3月21 日在亚服上线,届时玩家将前往阿拉希高地展开一场 60 人大逃杀对战。
艾泽拉斯的冒险者已经征服了艾泽拉斯的大地及遥远的彼岸。他们在对抗世界上最致命的敌人时展现出过人的手腕,并且成功阻止终结宇宙等级的威胁。当他们在为即将于《魔兽世界》资料片《地心之战》中来袭的萨拉塔斯势力做战斗准备时,他们还需要在熟悉的阿拉希高地面对一个全新的敌人──那就是彼此。在《巨龙崛起》10.2.6 更新的《强袭风暴》中,玩家将会进入一个全新的海盗主题大逃杀式限时活动,其中包含极高的风险和史诗级的奖励。
《强袭风暴》不是普通的战场,作为一个独立于主游戏之外的活动,玩家可以用大逃杀的风格来体验《魔兽世界》,不分职业、不分装备(除了你在赛局中捡到的),光是技巧和战略的强弱之分就能决定出谁才是能坚持到最后的赢家。本次活动将会开放单人和双人模式,玩家在加入海盗主题的预赛大厅区域前,可以从强袭风暴角色画面新增好友。游玩游戏将可以累计名望轨迹,《巨龙崛起》和《魔兽世界:巫妖王之怒 经典版》的玩家都可以获得奖励。
更新日志
- 凤飞飞《我们的主题曲》飞跃制作[正版原抓WAV+CUE]
- 刘嘉亮《亮情歌2》[WAV+CUE][1G]
- 红馆40·谭咏麟《歌者恋歌浓情30年演唱会》3CD[低速原抓WAV+CUE][1.8G]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[320K/MP3][193.25MB]
- 【轻音乐】曼托凡尼乐团《精选辑》2CD.1998[FLAC+CUE整轨]
- 邝美云《心中有爱》1989年香港DMIJP版1MTO东芝首版[WAV+CUE]
- 群星《情叹-发烧女声DSD》天籁女声发烧碟[WAV+CUE]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[FLAC/分轨][748.03MB]
- 理想混蛋《Origin Sessions》[320K/MP3][37.47MB]
- 公馆青少年《我其实一点都不酷》[320K/MP3][78.78MB]
- 群星《情叹-发烧男声DSD》最值得珍藏的完美男声[WAV+CUE]
- 群星《国韵飘香·贵妃醉酒HQCD黑胶王》2CD[WAV]
- 卫兰《DAUGHTER》【低速原抓WAV+CUE】
- 公馆青少年《我其实一点都不酷》[FLAC/分轨][398.22MB]
- ZWEI《迟暮的花 (Explicit)》[320K/MP3][57.16MB]