如何更改外键引用操作行为?

123

我已经设置了一个表格,其中包含一个带有外键的列,其设置为ON DELETE CASCADE(删除父项时删除子项)

如何使用SQL命令更改为ON DELETE RESTRICT?(如果有子项,则无法删除父项)

6个回答

199

虽然这个问题已经很旧了,但我还是想提供答案以帮助那些需要的人。

它是一个两步骤的过程:

假设table1有一个名为fk_table2_id外键,具有约束名fk_name,并且table2是被引用的表,其关键字为t2在我的图表中类似下面的内容)。

   table1 [ fk_table2_id ] --> table2 [t2]

第一步,删除旧的约束条件:(参考链接)

ALTER TABLE `table1` 
DROP FOREIGN KEY `fk_name`;  

注意约束被删除了,列并没有被删除

第二步,添加新的约束:

ALTER TABLE `table1`  
ADD CONSTRAINT `fk_name` 
    FOREIGN KEY (`fk_table2_id`) REFERENCES `table2` (`t2`) ON DELETE CASCADE;  

添加约束,该列已存在

示例:

我有一个引用Users表的UserDetails表:

mysql> SHOW CREATE TABLE UserDetails;
:
:
 `User_id` int(11) DEFAULT NULL,
  PRIMARY KEY (`Detail_id`),
  KEY `FK_User_id` (`User_id`),
  CONSTRAINT `FK_User_id` FOREIGN KEY (`User_id`) REFERENCES `Users` (`User_id`)
:
:

第一步:

mysql> ALTER TABLE `UserDetails` DROP FOREIGN KEY `FK_User_id`;
Query OK, 1 row affected (0.07 sec)  

第二步:

mysql> ALTER TABLE `UserDetails` ADD CONSTRAINT `FK_User_id` 
    -> FOREIGN KEY (`User_id`) REFERENCES `Users` (`User_id`) ON DELETE CASCADE;
Query OK, 1 row affected (0.02 sec)  

结果:

mysql> SHOW CREATE TABLE UserDetails;
:
:
`User_id` int(11) DEFAULT NULL,
  PRIMARY KEY (`Detail_id`),
  KEY `FK_User_id` (`User_id`),
  CONSTRAINT `FK_User_id` FOREIGN KEY (`User_id`) REFERENCES 
                                       `Users` (`User_id`) ON DELETE CASCADE
:

3
你认为应该添加的约束是 ON DELETE RESTRICT,就像原问题所请求的那样吗? - Noumenon
嗯,什么是“on delete cascade”,为什么需要它? - Lealo
3
当没有指定时,默认情况下是RESTRICT,因此您会得到它作为结果。 - edruid
1
@Lealo "on delete cascade" 的意思是,如果你从父表(在这种情况下是用户)中删除一行,则所有引用子表(UserDetails)的行也将被删除。 - edruid
1
感谢您的笔记:“注意约束已删除,列未删除”,“添加约束,列已存在”,我猜这意味着数据实际上被保留,只有模式发生了变化。 - George Birbilis

25

如果您愿意更改查询的名称,您可以在一个查询中完成此操作:

ALTER TABLE table_name
  DROP FOREIGN KEY `fk_name`,
  ADD CONSTRAINT `fk_name2` FOREIGN KEY (`remote_id`)
    REFERENCES `other_table` (`id`)
    ON DELETE CASCADE;

如果你有一个很大的表,这对于最小化停机时间非常有用。


新名称可能指示了新键的相当重要的属性 - fk_name_CASCADE - DJDave
遗憾的是,在MySQL/MariaDB中,如果新的外键名称与旧的相同,则此方法不起作用(“写入或更新时重复键”)。 - Dario Seidl

13
ALTER TABLE DROP FOREIGN KEY fk_name;
ALTER TABLE ADD FOREIGN KEY fk_name(fk_cols)
            REFERENCES tbl_name(pk_names) ON DELETE RESTRICT;

3
帮助我找到了解决方案 ALTER TABLE table_name ADD ... ON DELETE RESTRICT - Moak
3
不,"fk_name" 是约束名字。 提供一个是可选的。 我不确定,但也许你可以使用 SHOW CREATE TABLE 命令来检索它。 - pascal
1
ON CASCADE RESTRICT 可能不是预期的。 - jgreep

7

请记住,在删除外键后,MySQL会在列上保留一个简单的索引。因此,如果您需要更改 'references' 列,应该按照以下3个步骤进行:

  • 删除原始 FK
  • 删除索引(名称与以前的FK相同,使用drop index子句)
  • 创建新的 FK

关于删除索引的观点很好。如果我不删除索引,数据库会为外键创建重复的索引吗?还是它不会创建任何东西,只保留简单的索引? - Sinc

6

我有一堆需要更改的外键,所以我写了一个程序来帮我生成语句。想着我可以分享一下:

SELECT

CONCAT('ALTER TABLE `' ,rc.TABLE_NAME,
    '` DROP FOREIGN KEY `' ,rc.CONSTRAINT_NAME,'`;')
, CONCAT('ALTER TABLE `' ,rc.TABLE_NAME,
    '` ADD CONSTRAINT `' ,rc.CONSTRAINT_NAME ,'` FOREIGN KEY (`',kcu.COLUMN_NAME,
    '`) REFERENCES `',kcu.REFERENCED_TABLE_NAME,'` (`',kcu.REFERENCED_COLUMN_NAME,'`) ON DELETE CASCADE;')

FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS rc
LEFT OUTER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE kcu
    ON kcu.TABLE_SCHEMA = rc.CONSTRAINT_SCHEMA
    AND kcu.CONSTRAINT_NAME = rc.CONSTRAINT_NAME
WHERE DELETE_RULE = 'NO ACTION'
AND rc.CONSTRAINT_SCHEMA = 'foo'

1
如果一个约束涉及多列,那么这种方法将无法奏效。生成的 SQL 将为每个列创建单独的约束。 - lights
我所有的外键都在单个列上,所以我没有太考虑那种可能性,但是很好的想法。 - DavidSM

4
您可以使用一个查询来管理所有内容: ALTER TABLE products DROP FOREIGN KEY 旧的约束名称, ADD FOREIGN KEY (product_id, category_id) REFERENCES 外部表名 (外键名称,另一个使复合键) ON DELETE CASCADE ON UPDATE CASCADE

请注意保留HTML标记。

1
只有更改约束名才能使其起作用(如果使用自动生成的名称,则可能会起作用,猜测MySQL始终创建唯一的名称)。 - George Birbilis
查询在MySQL / MariaDB上肯定有效。关键在于按名称删除旧约束,这是在第2行完成的。 - stamster
1
当使用显式约束名时,MySQL中的全合一查询语法对我不起作用。 - George Birbilis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接