【问题标题】:Unique constraints across a many to many relationship table跨多对多关系表的唯一约束
【发布时间】:2018-06-29 07:16:24
【问题描述】:

有没有什么办法可以添加约束来确保一列中的 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 数字,它只会在伴随的情况下再次允许该数字相同的字符串。

是否有一个我可以声明的后台进程,比如添加一个唯一约束,或者它只需要由存储过程强加?

【问题讨论】:

  • MySQL 除了唯一键和外键之外没有任何内置约束。对于其他任何事情,您都必须使用检查条件的触发脚本。
  • 嗯好的。当我弄清楚我需要什么时,我想知道我的设计是否经过深思熟虑,但我什至无法选择一种更好地表示事物的方法,除非我制作一个查找表,这可能会导致大量重复并依赖于存储过程来无论如何都要查找...感觉可能有内置机制。还是谢谢!
  • 听起来很像您正在为一个相对简单的问题寻找一个非常复杂的解决方案。
  • 我正在寻找一个非常简单的解决方案!只是针对一个过于复杂的问题,我无法简化以找到简单的解决方案。
  • t2_pkt2_str 都放在关系表中怎么样?然后您可以在(t1_fk, t2_str) 上指定唯一约束。但是这种冗余违反了规范化,您需要实现一种方法,将t2_str 的更改从一个表传播到另一个表。

标签: mysql sql database database-design


【解决方案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;

Here你可以试试。

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

【讨论】:

  • 嗨,这是很棒的东西,我什至不明白很多,但我会把它作为公认的答案,并在我重新开始工作时正确地完成它。非常感谢您在这里的时间。
猜你喜欢
  • 2012-05-07
  • 2018-07-25
  • 2018-09-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-10
  • 1970-01-01
  • 2017-02-15
相关资源
最近更新 更多