聊聊从多个具有外键约束的表中删除数据行的问题

14619次阅读 803人点赞 作者: WuBin 发布时间: 2021-08-05 15:33:43
扫码到手机查看

数据表

首先创建数据库:

create database images_zt;
use images_zt;

比如有如下两个数据表(以下为主表):

create table list
(
    zt_id int unsigned not null auto_increment,
    title varchar(255) not null,
    created_at datetime not null,
    updated_at datetime not null,
    primary key(zt_id)
)ENGINE = InnoDB;
zt_id titlecreated_atupdated_at
1标题2021-08-09 15:00:002021-08-09 15:00:00

接下来是从表:

create table resource
(
    img_id int unsigned not null auto_increment,
    zt_id int unsigned not null,
    img_url text not null,
    created_at datetime not null,
    updated_at datetime not null,
    primary key(img_id),
    index (img_url),
    foreign key(zt_id) references list(zt_id)
)ENGINE = InnoDB;
img_idzt_idimg_urlcreated_atupdated_at
21xxx2021-08-09 15:00:002021-08-09 15:00:00

通过创建的SQL语句,我们可以看到,在resource表中,将List表的zt_id设置为了外键。《MYsql外键约束》

MySQL外键约束(FOREIGN KEY)是表的一个特殊字段,经常与主键约束一起使用。对于两个具有关联关系的表而言,相关联字段中主键所在的表就是主表(父表),外键所在的表就是从表(子表)。外键用来建立主表与从表的关联关系,为两个表的数据建立连接,约束两个表中数据的一致性和完整性。

这时候,如果我们仅仅从主表中删除一行:

 DELETE FROM list WHERE zt_id = 1;

这个时候一定会报错:

#1451 - Cannot delete or update a parent row: a foreign key constraint fails (`images_zt(这个为数据库)`.`resource`, CONSTRAINT `resource_ibfk_1` FOREIGN KEY (`zt_id`) REFERENCES `list` (`zt_id`))

大致翻译过来的意思就是:

无法删除或更新父行:外键约束失败(`images`.`resource`,约束`resource`外键(`zt_id`)引用`list`(`zt_ id`)

《MYsql外键约束》文章中我们知道,关于外键的创建,我们在另一篇文章再详细讨论

主表删除某条记录时,从表中与之对应的记录也必须有相应的改变。一个表可以有一个或多个外键,外键可以为空值,若不为空值,则每一个外键的值必须等于主表中主键的某个值。

所以问题原因就是表关联生成的强制约束问题,在删除的时候回检查表之间的关联关系,从而导致无法删除。

解决方法:临时关闭外键验证

在PHP中,我们可以通过执行sql语句先关闭外键验证,待执行删除后,再开启外键的验证。

MySQL的环境变量中存在一个foreign_key_checks,这是默认检查外键的配置项,如果将其设置为0,则表示不检查外键约束。查看foreign_key_checks的值:
show VARIABLES like "foreign%";

使用PHP执行:

// 先设置外键约束检查关闭
$sql_close_foreign_key_checks = "SET foreign_key_checks = 0";

// 删除主表的行或者表,如果要删除视图,也是如此
$sql_del = "DELETE FROM list WHERE zt_id = 1";

// 开启外键约束检查,以保持表结构完整性
$sql_open_foreign_key_checks = "SET foreign_key_checks = 1";

// DB::handle为数据库假想操作函数,执行sql语句
DB::handle($sql_close_foreign_key_checks);
$resDB = DB::handle($sql_del);
// 获取受影响行数,这里务必根据实际情况先保存一下,否则有可能被后面的操作覆盖结果
$del_ok = $resDB->rowCount();
// 再开启外键约束检查,以保持表结构完整性
DB::handle($sql_open_foreign_key_checks);
if($del_ok) {....}

解决方法:先删除从表中的行,再删主表的行

// 先删除从表中的行
DELETE FROM resource WHERE zt_id = 1;
// 当从表的行删除,这时删除主表的行就不会报错了
DELETE FROM list WHERE zt_id = 1;

解决方法:设计表时,添加on delete cascade

使用这种方法删除,叫做级联删除,在创建resource表的时候,需要做如下修改:

create table resource
(
    img_id int unsigned not null auto_increment,
    zt_id int unsigned not null,
    ...
    foreign key(zt_id) references list(zt_id) on delete cascade
)ENGINE = InnoDB;
级联删除(on delete set null 、  on delete cascade)属于外键关联的一种

这时候,直接删除主表中的数据,发现从表中的数据也被删除了。

关于多表删除,使用级联删除是一种方法,同时也可以使用存储例程、触发器方法去实现。

根据个人百度搜索得到的结果,一般是不推荐用数据库的级联的 在程序中用事务控制的删除好一些,也容易跟踪问题你用数据库级联,出了问题确实很难跟踪到。现在的趋势是 数据库的关系尽量的简单,尽量不要设外键之类的,所有的逻辑全部在代码中完成。

使用alter修改表:

alter table resource add foreign key(zt_id) references list(zt_id) on delete cascade;

不建议使用外键关系

阿里的JAVA规范中也有下面这一条:

【强制】不得使用外键与级联,一切外键概念必须在应用层解决。每次做DELETE 或者UPDATE都必须考虑外键约束,会导致开发的时候很痛苦,测试数据极为不方便。

更多的详情参见这篇文章:为什么不建议使用外键关系

相关资料

点赞 支持一下 觉得不错?客官您就稍微鼓励一下吧!
关键词:外键
推荐阅读
  • uniapp实现被浏览器唤起的功能

    当用户打开h5链接时候,点击打开app若用户在已经安装过app的情况下直接打开app,若未安装过跳到应用市场下载安装这个功能在实现上主要分为两种场景,从普通浏览器唤醒以及从微信唤醒。

    9094次阅读 588人点赞 发布时间: 2022-12-14 16:34:53 立即查看
  • Vue

    盘点Vue2和Vue3的10种组件通信方式

    Vue中组件通信方式有很多,其中Vue2和Vue3实现起来也会有很多差异;本文将通过选项式API组合式API以及setup三种不同实现方式全面介绍Vue2和Vue3的组件通信方式。

    3843次阅读 286人点赞 发布时间: 2022-08-19 09:40:16 立即查看
  • JS

    几个高级前端常用的API

    推荐4个前端开发中常用的高端API,分别是MutationObserver、IntersectionObserver、getComputedstyle、getBoundingClientRect、requ...

    14071次阅读 914人点赞 发布时间: 2021-11-11 09:39:54 立即查看
  • PHP

    【正则】一些常用的正则表达式总结

    在日常开发中,正则表达式是非常有用的,正则表达式在每个语言中都是可以使用的,他就跟JSON一样,是通用的。了解一些常用的正则表达式,能大大提高你的工作效率。

    12908次阅读 442人点赞 发布时间: 2021-10-09 15:58:58 立即查看
  • 【中文】免费可商用字体下载与考证

    65款免费、可商用、无任何限制中文字体打包下载,这些字体都是经过长期验证,经得住市场考验的,让您规避被无良厂商起诉的风险。

    11469次阅读 920人点赞 发布时间: 2021-07-05 15:28:45 立即查看
  • Vue

    Vue3开发一个v-loading的自定义指令

    在vue3中实现一个自定义的指令,有助于我们简化开发,简化复用,通过一个指令的调用即可实现一些可高度复用的交互。

    15588次阅读 1244人点赞 发布时间: 2021-07-02 15:58:35 立即查看
  • JS

    关于手机上滚动穿透问题的解决

    当页面出现浮层的时候,滑动浮层的内容,正常情况下预期应该是浮层下边的内容不会滚动;然而事实并非如此。在PC上使用css即可解决,但是在手机端,情况就变的比较复杂,就需要禁止触摸事件才可以。

    14795次阅读 1205人点赞 发布时间: 2021-05-31 09:25:50 立即查看
  • Vue

    Vue+html2canvas截图空白的问题

    在使用vue做信网单页专题时,有海报生成的功能,这里推荐2个插件:一个是html2canvas,构造好DOM然后转canvas进行截图;另外使用vue-canvas-poster(这个截止到2021年3月...

    28950次阅读 2273人点赞 发布时间: 2021-03-02 09:04:51 立即查看
  • Vue

    vue-router4过度动画无效解决方案

    在初次使用vue3+vue-router4时候,先后遇到了过度动画transition进入和退出分别无效的情况,搜遍百度没没找到合适解决方法,包括vue-route4有一些API都进行了变化,以前的一些操...

    24982次阅读 1925人点赞 发布时间: 2021-02-23 13:37:20 立即查看
交流 收藏 目录