一尘不染

多对多关系表的唯一约束

sql

是否有任何添加约束的方法来确保X在一个列中的输入只能允许Y在另一个列中的输入?

假设我有两个表,精简到最少的列,tbl_1有一个pk。tbl_2有2列-pk和文本字符串。

这些表由第三关系表连接,因为它们之间存在很多关系,并且使用tbl1和tbl2中的pk。

t1_pk     t2_pk | t2_str     t1fk | t2fk
  x         1       AAA        x      1
  y         2       BBB        x      2
  z         3       AAA        y      3  
            4       BBB        y      4
                               z      1
                               z      2

上面的所有条目都是允许的,但是现在我试图弄清楚如何限制关系表,以便附加到t2_pk的字符串只能绑定到t1_pk ONCE。例如,在第三张表中:

 t1fk | t2fk
   x      3

将不会被允许,因为x-1存在,并且1和3都附加了字符串AAA。我可以想到的一种无需再创建3个表并绕圈的方式是将字符串移动到关系表并添加约束,因此,如果表中已经存在t2fk数字,则如果再加上该数字,则仅允许该数字再次出现相同的字符串。

我可以声明是否有一个后台进程,例如添加一个唯一约束,还是仅需要由存储过程强加?


阅读 149

收藏
2021-05-16

共1个答案

一尘不染

您可以在包含关系的第三个表中添加t2_str列,或者为此目的创建一个新表。这里有一个示例,说明如何实现新表tab_constr。

drop table if exists tab_constr;
drop table if exists tab_rel;
drop table if exists tab_1;
drop table if exists tab_2;


CREATE TABLE tab_1 (
  t1_pk varchar(5),
  PRIMARY KEY (t1_pk)
);

CREATE TABLE tab_2 (
  t2_pk INT,
  t2_str varchar(10) NOT NULL,
  PRIMARY KEY (t2_pk),
  INDEX(t2_pk, t2_str)
);


CREATE TABLE tab_rel (
  t1_pk varchar(5),
  t2_pk INT,
  PRIMARY KEY (t1_pk,t2_pk),
  INDEX (t2_pk),
  FOREIGN KEY (t1_pk) REFERENCES tab_1(t1_pk),
  FOREIGN KEY (t2_pk) REFERENCES tab_2(t2_pk)
);

CREATE TABLE tab_constr (
  t1_pk varchar(5),
  t2_str varchar(10),
  t2_pk int,  
  PRIMARY KEY pair_already_exists(t1_pk,t2_str),
  INDEX(t1_pk, t2_pk),
  INDEX(t2_pk, t2_str),
  FOREIGN KEY (t1_pk, t2_pk) REFERENCES tab_rel(t1_pk, t2_pk)
  ON DELETE CASCADE,
  FOREIGN KEY (t2_pk, t2_str) REFERENCES tab_2(t2_pk, t2_str)
  ON UPDATE CASCADE
);


CREATE TRIGGER tr_ins_rel AFTER INSERT ON tab_rel
  FOR EACH ROW
  BEGIN
    INSERT INTO tab_constr ( t1_pk, t2_str, t2_pk)
        select new.t1_pk, t2_str, new.t2_pk
            from tab_2 
            where t2_pk=new.t2_pk
        ;
  END;

INSERT INTO tab_1 (t1_pk) VALUES ('x');
INSERT INTO tab_1 (t1_pk) VALUES ('y');
INSERT INTO tab_1 (t1_pk) VALUES ('z');
INSERT INTO tab_2 (t2_pk,t2_str) VALUES (1, 'AAA');
INSERT INTO tab_2 (t2_pk,t2_str) VALUES (2, 'BBB');
INSERT INTO tab_2 (t2_pk,t2_str) VALUES (3, 'AAA');
INSERT INTO tab_2 (t2_pk,t2_str) VALUES (4, 'BBB');
INSERT INTO tab_rel (t1_pk,t2_pk) VALUES ('x', 1);
INSERT INTO tab_rel (t1_pk,t2_pk) VALUES ('x', 2);
INSERT INTO tab_rel (t1_pk,t2_pk) VALUES ('y', 3);
INSERT INTO tab_rel (t1_pk,t2_pk) VALUES ('y', 4);
INSERT INTO tab_rel (t1_pk,t2_pk) VALUES ('z', 1);
INSERT INTO tab_rel (t1_pk,t2_pk) VALUES ('z', 2);
commit;

以下语句将引发错误:

INSERT INTO tab_rel (t1_pk,t2_pk) VALUES ('x', 3);

这也会引发错误

UPDATE tab_2 set t2_str='BBB' where t2_pk=1;

但这会起作用

DELETE FROM tab_rel where t1_pk='x' and t2_pk=1;
INSERT INTO tab_rel (t1_pk,t2_pk) VALUES ('x', 3);

这将起作用

UPDATE tab_2 set t2_str='XXX' where t2_pk=1;

在这里您可以尝试一下。

当然,该附加表违反了正常形式,并为数据库增加了冗余。但这没问题,因为此表tab_constr是一个辅助结构,如索引,并且它将由数据库自动维护。因此,不会发生插入/更新/删除异常。

2021-05-16