MySQL外键约束,级联删除

我想使用外键来保持完整性并避免孤儿(我已经使用innoDB)。

如何创建一个SQL语句DELETE ON CASCADE?

如果我删除一个类别,那么我如何确保它不会删除也与其他类别相关的产品。

数据透视表“categories_products”在另外两个表之间创建了一个多对多的关系。

categories - id (INT) - name (VARCHAR 255) products - id - name - price categories_products - categories_id - products_id 

如果你的级联删除了一个产品,因为它是一个被杀的类别的成员,那么你已经设置了不正确的外键。 给定您的示例表,您应该有以下表设置:

 CREATE TABLE categories ( id int unsigned not null primary key, name VARCHAR(255) default null )Engine=InnoDB; CREATE TABLE products ( id int unsigned not null primary key, name VARCHAR(255) default null )Engine=InnoDB; CREATE TABLE categories_products ( category_id int unsigned not null, product_id int unsigned not null, PRIMARY KEY (category_id, product_id), KEY pkey (product_id), FOREIGN KEY (category_id) REFERENCES categories (id) ON DELETE CASCADE ON UPDATE CASCADE, FOREIGN KEY (product_id) REFERENCES products (id) ON DELETE CASCADE ON UPDATE CASCADE )Engine=InnoDB; 

这样,您可以删除产品或类别,并且只有类别产品中的关联记录将会一起死掉。 级联将不会继续向上行,并删除父级产品/类别表。

例如

 products: boots, mittens, hats, coats categories: red, green, blue, white, black prod/cats: red boots, green mittens, red coats, black hats 

如果您删除了“红色”类别,则只有类别表格中的“红色”条目以及两个条目prod / cats:“红色靴子”和“红色外套”。

删除不会进一步级联,也不会取出“靴子”和“大衣”类别。

评论跟进:

你仍然误解如何级联删除工作。 它们只影响“on delete cascade”定义的表。 在这种情况下,级联设置在“categories_products”表中。 如果删除“红色”类别,那么category_product中将级联删除的唯一记录是category_id = red 。 它不会触及'category_id = blue'的任何记录,并且不会向前移动到“products”表,因为在该表中没有定义外键。

这是一个更具体的例子:

 categories: products: +----+------+ +----+---------+ | id | name | | id | name | +----+------+ +----+---------+ | 1 | red | | 1 | mittens | | 2 | blue | | 2 | boots | +---++------+ +----+---------+ products_categories: +------------+-------------+ | product_id | category_id | +------------+-------------+ | 1 | 1 | // red mittens | 1 | 2 | // blue mittens | 2 | 1 | // red boots | 2 | 2 | // blue boots +------------+-------------+ 

假设您删除类别#2(蓝色):

 DELETE FROM categories WHERE (id = 2); 

DBMS将查看具有指向“类别”表的外键的所有表,并删除匹配的id为2的记录。由于我们仅在products_categories定义了外键关系,因此您最终会得到此表一次删除完成:

 +------------+-------------+ | product_id | category_id | +------------+-------------+ | 1 | 1 | // red mittens | 2 | 1 | // red boots +------------+-------------+ 

products表中没有定义外键,所以级联不能在那里工作,所以你还是会列出靴子和手套。 没有“蓝色的靴子”,也没有“蓝色的手套”。

我对这个问题的答案感到困惑,所以我在MySQL中创建了一个测试用例,希望这有帮助

 -- Schema CREATE TABLE T1 ( `ID` int not null auto_increment, `Label` varchar(50), primary key (`ID`) ); CREATE TABLE T2 ( `ID` int not null auto_increment, `Label` varchar(50), primary key (`ID`) ); CREATE TABLE TT ( `IDT1` int not null, `IDT2` int not null, primary key (`IDT1`,`IDT2`) ); ALTER TABLE `TT` ADD CONSTRAINT `fk_tt_t1` FOREIGN KEY (`IDT1`) REFERENCES `T1`(`ID`) ON DELETE CASCADE, ADD CONSTRAINT `fk_tt_t2` FOREIGN KEY (`IDT2`) REFERENCES `T2`(`ID`) ON DELETE CASCADE; -- Data INSERT INTO `T1` (`Label`) VALUES ('T1V1'),('T1V2'),('T1V3'),('T1V4'); INSERT INTO `T2` (`Label`) VALUES ('T2V1'),('T2V2'),('T2V3'),('T2V4'); INSERT INTO `TT` (`IDT1`,`IDT2`) VALUES (1,1),(1,2),(1,3),(1,4), (2,1),(2,2),(2,3),(2,4), (3,1),(3,2),(3,3),(3,4), (4,1),(4,2),(4,3),(4,4); -- Delete DELETE FROM `T2` WHERE `ID`=4; -- Delete one field, all the associated fields on tt, will be deleted, no change in T1 TRUNCATE `T2`; -- Can't truncate a table with a referenced field DELETE FROM `T2`; -- This will do the job, delete all fields from T2, and all associations from TT, no change in T1 

我认为(我不确定)外键约束不会完全按照您希望的表格设计进行。 也许最好的办法是定义一个存储过程,以你想要的方式删除一个类别,然后每当你想删除一个类别时调用该过程。

 CREATE PROCEDURE `DeleteCategory` (IN category_ID INT) LANGUAGE SQL NOT DETERMINISTIC MODIFIES SQL DATA SQL SECURITY DEFINER BEGIN DELETE FROM `products` WHERE `id` IN ( SELECT `products_id` FROM `categories_products` WHERE `categories_id` = category_ID ) ; DELETE FROM `categories` WHERE `id` = category_ID; END 

您还需要将以下外键约束添加到链接表中:

 ALTER TABLE `categories_products` ADD CONSTRAINT `Constr_categoriesproducts_categories_fk` FOREIGN KEY `categories_fk` (`categories_id`) REFERENCES `categories` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT `Constr_categoriesproducts_products_fk` FOREIGN KEY `products_fk` (`products_id`) REFERENCES `products` (`id`) ON DELETE CASCADE ON UPDATE CASCADE 

CONSTRAINT子句当然也可以出现在CREATE TABLE语句中。

创建了这些模式对象之后,您可以通过发出CALL DeleteCategory(category_ID) (其中category_ID是要删除的CALL DeleteCategory(category_ID)来删除一个类别并获得您想要的行为,并且将以您想要的方式运行。 但是不要发出一个正常的DELETE FROM查询,除非你想要更多的标准行为(例如,只从链接表中删除,并保留products表单独)。