I have a simple InnoDB table:
CREATE TABLE `T` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`deleted` bigint(20) unsigned NOT NULL DEFAULT '0',
`other` varchar(32) DEFAULT NULL,
PRIMARY KEY (`id`,`deleted`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1;
And another table that encodes relationships among entries from the first table:
CREATE TABLE `T_T` (
`id1` bigint(20) unsigned NOT NULL,
`deleted1` bigint(20) unsigned NOT NULL,
`id2` bigint(20) unsigned NOT NULL,
`deleted2` bigint(20) unsigned NOT NULL,
PRIMARY KEY (`id1`,`deleted1`,`id2`,`deleted2`),
KEY `fk_T_T_2_idx` (`id2`,`deleted2`),
CONSTRAINT `fk_T_T_2` FOREIGN KEY (`id2`, `deleted2`) REFERENCES `T` (`id`, `deleted`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `fk_T_T_1` FOREIGN KEY (`id1`, `deleted1`) REFERENCES `T` (`id`, `deleted`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Creation of tables and population works perfectly fine.
INSERT INTO T (id, deleted, other) VALUES (1, 0, 'whatever');
INSERT INTO T_T (id1, deleted1, id2, deleted2) VALUES (1, 0, 1, 0);
Updating an entry in T, however, with something like this:
UPDATE T SET deleted = 1 WHERE id = 1;
leads to the following error:
ERROR 1452: 1452: Cannot add or update a child row: a foreign key constraint fails (`test`.`T_T`, CONSTRAINT `fk_T_T_1` FOREIGN KEY (`id1`, `deleted1`) REFERENCES `T` (`id`, `deleted`) ON DELETE CASCADE ON UPDATE CASCADE)
This works if the entries in T_T don't refer to self (id1 != id2). But an UPDATE will fail when a row in T_T refers with both ids to the same row in T.
Is there a problem in MySQL with foreign keys that refer to another table's column(s) twice? Is there a work around for something like this?
It almost appears that the foreign keys in T_T are updated separately. The sequence of internal MySQL operations appears to be: 1) update the record in T; b) cascade to the first foreign key in T_T; c) cascade to the second foreign key in T_T. But after step b) the second's foreign key constraints fail because it now points to a non-existent row in T. Does that make sense?
I can perform the update of T when foreign_key_checks is set to 0 for the duration of the UPDATE call. But then this prevents cascading of this update to other tables, which sort of defeats the purpose of cascading...
CREATE TABLE `T` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`deleted` bigint(20) unsigned NOT NULL DEFAULT '0',
`other` varchar(32) DEFAULT NULL,
PRIMARY KEY (`id`,`deleted`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1;
And another table that encodes relationships among entries from the first table:
CREATE TABLE `T_T` (
`id1` bigint(20) unsigned NOT NULL,
`deleted1` bigint(20) unsigned NOT NULL,
`id2` bigint(20) unsigned NOT NULL,
`deleted2` bigint(20) unsigned NOT NULL,
PRIMARY KEY (`id1`,`deleted1`,`id2`,`deleted2`),
KEY `fk_T_T_2_idx` (`id2`,`deleted2`),
CONSTRAINT `fk_T_T_2` FOREIGN KEY (`id2`, `deleted2`) REFERENCES `T` (`id`, `deleted`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `fk_T_T_1` FOREIGN KEY (`id1`, `deleted1`) REFERENCES `T` (`id`, `deleted`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Creation of tables and population works perfectly fine.
INSERT INTO T (id, deleted, other) VALUES (1, 0, 'whatever');
INSERT INTO T_T (id1, deleted1, id2, deleted2) VALUES (1, 0, 1, 0);
Updating an entry in T, however, with something like this:
UPDATE T SET deleted = 1 WHERE id = 1;
leads to the following error:
ERROR 1452: 1452: Cannot add or update a child row: a foreign key constraint fails (`test`.`T_T`, CONSTRAINT `fk_T_T_1` FOREIGN KEY (`id1`, `deleted1`) REFERENCES `T` (`id`, `deleted`) ON DELETE CASCADE ON UPDATE CASCADE)
This works if the entries in T_T don't refer to self (id1 != id2). But an UPDATE will fail when a row in T_T refers with both ids to the same row in T.
Is there a problem in MySQL with foreign keys that refer to another table's column(s) twice? Is there a work around for something like this?
It almost appears that the foreign keys in T_T are updated separately. The sequence of internal MySQL operations appears to be: 1) update the record in T; b) cascade to the first foreign key in T_T; c) cascade to the second foreign key in T_T. But after step b) the second's foreign key constraints fail because it now points to a non-existent row in T. Does that make sense?
I can perform the update of T when foreign_key_checks is set to 0 for the duration of the UPDATE call. But then this prevents cascading of this update to other tables, which sort of defeats the purpose of cascading...