如何更新链接到多个表的 FK - 更新时的级联

Posted

技术标签:

【中文标题】如何更新链接到多个表的 FK - 更新时的级联【英文标题】:How to update FK linked to multiple table - Cascade on Update 【发布时间】:2019-01-12 00:53:27 【问题描述】:

我有 3 个相互链接的表

    任务 客户 合规

关系如下图所示

compliance 表的外键如下

task 表的外键如下

问题: 当我在client 表中编辑/更新clientno 时,我得到了

1452: Cannot add or update a child row: a foreign key constraint fails
(`task`, CONSTRAINT `task_ibfk_1` FOREIGN KEY (`officeid`, `clientid`) REFERENCES `client` (`officeid`, `clientno`) ON UPDATE CASCADE)

我预计,当 client 表中的 clientno 发生更改时,complaincetask 表中的相同内容也会更新。

我想我遇到了 InnoDB 引擎的一个已知限制。这不允许对 FK 进行级联更新。如果这是真的,那么用新的clientno 更新 3 个表的解决方案是什么?

编辑 1: 正如@pankaj 指出的,如何克服

如果 ON UPDATE CASCADE 递归更新它之前在级联期间更新的同一个表,它的行为类似于 RESTRICT。这意味着您不能使用自引用的 ON UPDATE CASCADE 操作。这是为了防止级联更新导致无限循环。

编辑 2:

create table client
(
  officeid     char(6)                 not null,
  clientno     char(10)                not null,
  fname        varchar(40)             not null,
  primary key (officeid, clientno)
);

create index officeid_clientno
  on client (officeid, clientno);


create table compliance
(
  officeid    char(6)                   not null,
  id          smallint(5) unsigned      not null,
  clientid    char(10)                  not null,
  primary key (officeid, id),
  constraint compliance_ibfk_2
  foreign key (officeid, clientid) references client (officeid, clientno)
    on update cascade
    on delete cascade
);

create index officeid_clientid
  on compliance (officeid, clientid, id);


create table task
(
  officeid        char(6)                      not null,
  taskno          char(10)                     not null,
  clientid        char(10)                     not null,
  taskname        varchar(50)                  not null,
  complianceid    smallint(5) unsigned         null,
  primary key (officeid, taskno),
  constraint task_ibfk_1
  foreign key (officeid, clientid) references client (officeid, clientno)
    on update cascade,
  constraint task_ibfk_4
  foreign key (officeid, clientid, complianceid) references compliance (officeid, clientid, id)
    on update cascade
);

create index officeid_clientid_complianceid
  on task (officeid, clientid, complianceid);

仅供参考:我在 mariadb 10.3 和 mysql 8.0 中尝试过

【问题讨论】:

这可能会有所帮助 - ***.com/a/5446693/4050261 @GMB,添加表创建语句 taskcompliance 创建一个包含officeidclientid 的外键没有意义,因为您已经将这些列包含在client 的外键中(这是适当的表)(并且compliance 已经具有相同的外键关系)。您应该将该外键更改为仅位于 complianceid 问题在this db fiddlethis db fiddleMariaDB 10.3 上重现 @Nick,这将导致Task 表中的记录与Compliance 表中的记录不一样clientid 【参考方案1】:

问题与声明关系的方式有关。

首先,正如@Nick 所评论的,taskclient 之间不需要关系,因为与compliance 的关系已经涵盖了这一点。注释这个多余约束的声明就足以使错误消失,正如您在 this db fiddle 中看到的那样。

create table task
(
  officeid        char(6)                      not null,
  ...
  primary key (officeid, taskno),
  -- constraint task_ibfk_1
   -- foreign key (officeid, clientid) references client (officeid, clientno)
   -- on update cascade,
  constraint task_ibfk_4
  foreign key (officeid, clientid, complianceid) references compliance (officeid,     clientid, id)
    on update cascade
); 

另一个建议是在所有表中使用自动递增的主键(您可以使用UNIQUE 索引来强制执行复合参照完整性规则)。这是处理 MySQL 的最常用方法,处理关系非常简单。

【讨论】:

并非所有任务都通过Compliance 表链接到Client 表。因此complianceidTask 表中可以为空。感谢您创建小提琴。我已经添加了我的 sql 语句。 dbfiddle.uk/… 我同意@AdarshMadrecha,需要 2 个 FK。第一个到Client 表和另一个到Complaince 表使用clientid & complianceid【参考方案2】:

我认为您的问题源于使用可变字段作为主键

您可以通过使用代理不可变主键并向可变字段添加唯一键来缓解这种情况。您应该能够在不影响数据完整性的情况下应用与以前相同的约束

例如:

CREATE TABLE client (
  id INT(10) UNSIGNED NOT NULL AUTO-INCREMENT PRIMARY,
  officeid CHAR(6) NOT NULL,
  clientno CHAR(10) NOT NULL,
  fname VARCHAR(40) NOT NULL
);

CREATE UNIQUE INDEX uq-client-officeid-clientno IN client (officeid, clientno);

CREATE TABLE compliance (
  id SMALLINT(5) UNSIGNED NOT NULL AUTO-INCREMENT PRIMARY,
  client_id INT(10) UNSIGNED NOT NULL,
  CONSTRAINT fk-compliance-client-id FOREIGN KEY id 
    REFERENCES client (id)
);

CREATE INDEX ix-compliance-id-client_id IN compliance (id, client_id);

CREATE TABLE task (
  id INT(10) UNSIGNED NOT NULL AUTO-INCREMENT PRIMARY,
  client_id INT(10) UNSIGNED NOT NULL,
  compliance_id SMALLINT(5) UNSIGNED NULL,
  taskno CHAR(10) NOT NULL,
  taskname VARCHAR(50) NOT NULL,
  CONSTRAINT fk-task-client-id FOREIGN KEY id 
    REFERENCES client (id),
  CONSTRAINT fk-task-compliance-id-client_id FOREIGN KEY (compliance_id, client_id) 
    REFERENCES compliance (id, client_id)
);

此表结构模仿您当前的约束,并允许您更新 clientno 而无需级联

注意外键 fk-task-compliance-id-client_id 确保任务引用的合规性包含正确的 client_id

我还会考虑一个单独的表 office,它具有代理整数主键并包含基于字符的 officeid。这可以被客户端表引用

【讨论】:

以上是关于如何更新链接到多个表的 FK - 更新时的级联的主要内容,如果未能解决你的问题,请参考以下文章

sql中如何实现级联表的操作

SqlServer触发器实现表的级联插入级联更新

关于数据的级联删除和更新

PythonDjango数据模型级联删除级联更新ER图导出等

Access 中的级联字段加入

如何在不加载视图的情况下重新加载/刷新部分视图中的级联下拉列表