Reputation: 5303
Is there any way to add a constraint to ensure that an entry of X in one column can then only allow an entry of Y in another column?
Say I have two tables, stripped down to minimal columns, tbl_1 has a pk. tbl_2 has 2 columns - a pk and a text string.
These tables are joined by a 3rd relationship table because they're many to many and it uses pk from tbl1 and tbl2.
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
All entries above are allowed, but now I'm trying to figure out how I can constrict the relationship table so that the string attached to t2_pk can only tie in to the t1_pk ONCE. E.g. in the 3rd table:
t1fk | t2fk
x 3
would not be allowed because x-1 exists and both 1 and 3 have the string AAA attached. One way I can think without making 3 more tables and going round in circles, is to move the string to the relationship table and add a constraint so if the t2fk number exists in the table already it'll only allow the number again if accompanied by the same string.
is there a background process for this I can declare like adding a unique constraint, or would it simply need to be imposed by a stored procedure?
Upvotes: 2
Views: 6181
Reputation: 1973
Either you add a t2_str column to your third table that contains the relations or you create a new table for this purpose. Here an example how to implement the new table 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;
The following statement will raise an error:
INSERT INTO tab_rel (t1_pk,t2_pk) VALUES ('x', 3);
This also raises an error
UPDATE tab_2 set t2_str='BBB' where t2_pk=1;
But this will work
DELETE FROM tab_rel where t1_pk='x' and t2_pk=1;
INSERT INTO tab_rel (t1_pk,t2_pk) VALUES ('x', 3);
and this will work
UPDATE tab_2 set t2_str='XXX' where t2_pk=1;
Here you can try it out.
Of course this additional table violates normal form and adds redundancy to your database. But that is no problem because this table tab_constr is an auxiliary structure like an index and it will be automatically maintained by the database. So no insert/update/delete anomalies will occur.
Upvotes: 3