如何在MySQL中暂时禁用外键约束?


651

是否可以在MySQL中暂时禁用约束?

我有两个Django模型,每个模型都有一个外键。由于ForeignKey约束,删除模型实例将返回错误:

cursor.execute("DELETE FROM myapp_item WHERE n = %s", n)
transaction.commit_unless_managed()  #a foreign key constraint fails here

cursor.execute("DELETE FROM myapp_style WHERE n = %s", n)
transaction.commit_unless_managed()

是否可以暂时禁用约束并仍然删除?


3
我要么不明白你想做什么,要么我想做的事情非常非常非常丑陋。即使可以做到,您也可能不应该这样做。
Dariusz

3
删除并重新应用FK 更改您的数据库。您正在尝试克服使系统具有某种意义的非常严格的约束条件,无需考虑FK可能是暂时的事情,如果知道的话,它会感到恐慌。
Grant Thomas

1
您尝试做的事情很奇怪。但是,您正在使用哪个数据库?
andrefsp

4
如果不是将约束永久禁用,而是禁用了约束,该ON DELETE SET NULL怎么办?那将完成类似的事情,而您不必打开和关闭按键检查。
dnagirl 2013年

1
@dnagirl:的确会更好。我怎样才能做到这一点?
2013年7

Answers:


1466

尝试DISABLE KEYS

SET FOREIGN_KEY_CHECKS=0;

确保

SET FOREIGN_KEY_CHECKS=1;

后。


14
这是为整个mysql设置的还是只是为会话设置的?
tipu

28
我相信这是每节课。
安德鲁·坎贝尔


1
我可以仅禁用单个表的FOREIGN_KEY_CHECKS吗?
jDub9

@Pacerier通过阅读,看来可以,但仅适用于单个会话。
Brett

150

要全局关闭外键约束,请执行以下操作:

SET GLOBAL FOREIGN_KEY_CHECKS=0;

并记得在完成后将其重新设置

SET GLOBAL FOREIGN_KEY_CHECKS=1;

警告:仅在进行单用户模式维护时才应这样做。因为这可能导致数据不一致。例如,当您使用mysqldump输出上传大量数据时,这将非常有用。


1
这是我需要知道的,所以它不是很好的做法,但是这些家伙的得分应该更高……
ftrotter 2014年

1
在尝试“最佳答案”后,这对我没有用。也许可以添加差异的解释。
hexnet

7
@hexnet的区别在于,仅SET FOREIGN_KEY_CHECKS更改当前连接SET GLOBAL ..的值,而更改所有连接(包括将来的连接)的值。如果仅SET FOREIGN..在一个窗口中执行该操作,然后尝试在其他窗口(通过不同的连接)中应用该语句,则该值在此处未更改。使用GLOBAL,两个连接的相同变量具有相同的值。
MatsLindh '16

播放时,可以帮助我的唯一的事更大的转储(6+ GB)<3
马克斯

这对我不起作用。当我尝试时,我会看到:ERROR 1228 (HY000): Variable 'foreign_key_checks' is a SESSION variable and can't be used with SET GLOBAL
Mike B

53

我通常只在想截断表时才禁用外键约束,并且由于我不断回到这个答案,所以这是给我将来的:

SET FOREIGN_KEY_CHECKS=0;
TRUNCATE TABLE table;
SET FOREIGN_KEY_CHECKS=1;

25

与其禁用您的约束,不如将其永久修改为ON DELETE SET NULL。这将完成类似的事情,而您不必打开和关闭按键检查。像这样:

ALTER TABLE tablename1 DROP FOREIGN KEY fk_name1; //get rid of current constraints
ALTER TABLE tablename2 DROP FOREIGN KEY fk_name2;

ALTER TABLE tablename1 
  ADD FOREIGN KEY (table2_id) 
        REFERENCES table2(id)
        ON DELETE SET NULL  //add back constraint

ALTER TABLE tablename2 
  ADD FOREIGN KEY (table1_id) 
        REFERENCES table1(id)
        ON DELETE SET NULL //add back other constraint

阅读此内容(http://dev.mysql.com/doc/refman/5.5/en/alter-table.html)和此内容(http://dev.mysql.com/doc/refman/5.5/en /create-table-foreign-keys.html)。


7
注意更改表可能要花费很长时间,最好将服务器全局设置为FOREIGN_KEY_CHECKS0,并在完成脏工作后将其放回去。此外,它可能会锁定用于编写表的位置。
Aki

更改远程列类型时,这不会破坏参考吗?(似乎我的客户将修改后的临时表重命名为原始表名。)
Cees Timmerman


10

phpmyadmin一个非常简单的解决方案:

  • 在表格中,转到SQL标签
  • 编辑要运行的SQL命令后,旁边有一个复选框GO,名为“ 启用外键检查”
  • 取消选中此复选框,然后运行SQL。执行后将自动重新检查。

3
谢谢!实际上,解决方案SET FOREIGN_KEY_CHECKS=0; ..... SET FOREIGN_KEY_CHECKS=1;在PHPMyAdmin中对我不起作用,因为我忘记了取消选中“启用外键检查”复选框。在PHPMyAdmin中,您可以跳过这些SET命令,而只需取消选中该复选框。
1

5

对我来说SET FOREIGN_KEY_CHECKS=0;还不够。我仍然有一个com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException

我必须补充ALTER TABLE myTable DISABLE KEYS;

所以:

SET FOREIGN_KEY_CHECKS=0;
ALTER TABLE myTable DISABLE KEYS;
DELETE FROM myTable;
ALTER TABLE myTable ENABLE KEYS;
SET FOREIGN_KEY_CHECKS=1;

仅供参考,mySQL 5.7引发警告,运行DISABLE KEYS命令时InnoDB引擎没有此选项。
jDub9

这确实有效,但是没有alter table对我也不起作用
David Kabii

3

如果键字段可为空,则还可以在尝试删除它之前将值设置为null:

cursor.execute("UPDATE myapp_item SET myapp_style_id = NULL WHERE n = %s", n)
transaction.commit_unless_managed() 

cursor.execute("UPDATE myapp_style SET myapp_item_id = NULL WHERE n = %s", n)
transaction.commit_unless_managed()

cursor.execute("DELETE FROM myapp_item WHERE n = %s", n)
transaction.commit_unless_managed()

cursor.execute("DELETE FROM myapp_style WHERE n = %s", n)
transaction.commit_unless_managed()

2

在phpMyAdmin中,您可以选择多行,然后单击Delete操作。您将进入一个列出删除查询的屏幕,您可以取消选中外键检查,然后单击“是”执行它们。

即使存在ON DELETE限制约束,这也使您能够删除行。


-2

将外键约束设置为0不是一个好主意,因为如果这样做,数据库将无法确保它不会违反参照完整性。这可能会导致数据不准确,误导或不完整。

生成外键是有原因的:因为子列中的所有值都应与父列中的值相同。如果没有外键约束,则子行可以具有不在父行中的值,这将导致数据不正确。

例如,假设您有一个供学生登录的网站,并且每个学生都必须注册一个用户帐户。您有一个用于用户ID的表,其中用户ID作为主键。和另一个用于学生帐户的表格,其中以学生ID为列。由于每个学生都必须有一个用户ID,因此将来自学生帐户表的学生ID用作引用用户ID表中主键用户ID的外键是有意义的。如果没有外键检查,则学生最终可能会获得一个学生ID和一个用户ID,这意味着该学生可以在没有用户身份的情况下获得帐户,这是错误的。

试想一下,如果它发生在大量数据上。这就是为什么需要外键检查的原因。

最好找出导致错误的原因。最有可能的是,您尝试从父行中删除而不从子行中删除。尝试从子行删除,然后再从父行删除。


是的,总会有一个权衡。
Pacerier

21
没有人说要永远这样下去。您可以关闭约束,批量加载一些数据,然后再将其重新打开。没什么大不了的,人们一直在做。
bwawok

批量进口是必需的,至少对于性能而言,这是很常见的。有时您只需要还原数据,然后可以进行检查。
Firas Abd Alrahman'2

3
这不是问题的答案。
Koray Tugay

请注意,他的问题是如何临时执行此操作。进行某些维护和数据导入时,这是必需的。当然,需要注意的是,导入脚本对数据完整性负责。然后,稍后重新打开索引和约束时,数据库将告诉您是否发生了故障。
mcstar
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.