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

时间:2019-01-12 00:53:28

标签: mysql sql mariadb cascade referential-integrity

我有3个相互链接的表

  1. 任务
  2. 客户
  3. 遵守

关系如下图所示 relationship

compliance表具有如下外键 compliance table

task表具有如下外键 task table

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

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)

我希望在clientno表中更改client时,在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中都尝试过

2 个答案:

答案 0 :(得分:3)

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

首先,如@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进行处理的最常用的方法,通过这种方法可以很轻松地处理关系。

答案 1 :(得分:0)

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

您可以通过使用代理不可变主键并将唯一键添加到可变字段来减轻这种情况。您应该能够像以前一样应用相同的约束,而不会影响数据完整性

例如:

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。然后可以由客户表引用