如何在 MySQL 中临时禁用外键约束?
Posted
技术标签:
【中文标题】如何在 MySQL 中临时禁用外键约束?【英文标题】:How can I temporarily disable a foreign key constraint in MySQL? 【发布时间】:2017-01-06 07:11:28 【问题描述】:是否可以在 mysql 中暂时禁用约束?
我有两个 Django 模型,每个模型都有一个指向另一个模型的外键。由于外键约束,删除模型实例会返回错误:
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()
是否可以暂时禁用约束并删除?
【问题讨论】:
要么我不明白你想做的事,要么你想做的事非常、非常、非常难看。即使你能做到,你也不应该这样做。 删除并重新应用 FK 正在更改您的数据库。你试图挑战那些让系统看到某种意义的限制,它没有考虑到 FK 可能是一个临时的东西,如果它知道,它会恐慌。 你想做什么很奇怪。但是您使用的是哪个数据库? 如果您没有禁用约束,而是将其永久修改为ON DELETE SET NULL
,该怎么办?这将完成类似的事情,您不必打开和关闭密钥检查。
@dnagirl:确实会更好。我该怎么做?
【参考方案1】:
在phpMyAdmin 中,您可以选择多行,然后可以单击删除操作。您将进入一个列出删除查询的屏幕。它看起来像这样:
请取消选中“启用外键检查”复选框,然后单击是执行它们。
这将使您能够删除行,即使存在 ON DELETE 限制约束。
【讨论】:
【参考方案2】:全局关闭外键约束:
SET GLOBAL FOREIGN_KEY_CHECKS = 0;
对于活动外键约束:
SET GLOBAL FOREIGN_KEY_CHECKS = 1;
【讨论】:
【参考方案3】:phpMyAdmin 的一个非常简单的解决方案:
在您的表中,转到 SQL 选项卡 编辑要运行的 SQL 命令后,GO 旁边有一个复选框,名为“启用外键检查”。 取消选中此复选框并运行您的 SQL。执行后会自动复查。【讨论】:
谢谢!确实解决方案SET FOREIGN_KEY_CHECKS=0; ..... SET FOREIGN_KEY_CHECKS=1;
在 PHPMyAdmin 中对我不起作用,因为我忘记取消选中“启用外键检查”复选框。在 PHPMyAdmin 中,您可以跳过这些 SET 命令并取消选中复选框。【参考方案4】:
试试DISABLE KEYS
或
SET FOREIGN_KEY_CHECKS=0;
确保
SET FOREIGN_KEY_CHECKS=1;
之后。
【讨论】:
这是为整个 mysql 设置的还是只是那个会话? 我相信是每次会话。 serverfault.com/questions/291100/… ,还要注意你 cannotdisable keys
代表 Innodb
我可以只为单个表禁用 FOREIGN_KEY_CHECKS 吗?
澄清@Pacerier 的评论:对于Innodb,您不能DISABLE KEYS
。但是您可以使用显示的其他命令完成此问题中的要求:SET FOREIGN_KEY_CHECKS=0;
(起初我以为他说没有办法做到这一点。)【参考方案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 抛出警告,InnoDB 引擎在运行 DISABLE KEYS 命令时没有此选项。 这确实有效,但没有 alter table 它对我也不起作用【参考方案6】:将外键约束设置为 0 不是一个好主意,因为如果这样做,您的数据库将无法确保它不违反参照完整性。这可能会导致数据不准确、误导或不完整。
您创建外键是有原因的:因为子列中的所有值都应与父列中的值相同。如果没有外键约束,子行的值可能不在父行中,这会导致数据不准确。
例如,假设您有一个供学生登录的网站,并且每个学生都必须以用户身份注册一个帐户。您有一个用户 ID 表,用户 ID 作为主键;和另一个学生帐户表,以学生 ID 作为列。由于每个学生都必须有一个用户 ID,因此将学生帐户表中的学生 ID 设为引用用户 ID 表中主键用户 ID 的外键是有意义的。如果没有外键检查,一个学生最终可能会得到一个学生 id 而没有用户 id,这意味着一个学生可以在没有成为用户的情况下获得一个帐户,这是错误的。
想象一下,如果它发生在大量数据上。这就是您需要外键检查的原因。
最好找出导致错误的原因。最有可能的是,您尝试从父行中删除而不从子行中删除。在从父行删除之前尝试从子行中删除。
【讨论】:
诚然,总会有取舍。 没有人说要永远这样运行它。您关闭约束,批量加载一些数据,然后将其重新打开。没什么大不了的,人们一直都在这样做。 批量导入是必须的,至少为了性能,很常见。有时您只需要恢复数据,然后您就可以进行检查。 这不是问题的答案。 注意,他的问题是暂时怎么做。这在进行某些维护和数据导入时是必需的。当然需要注意的是,您的导入脚本将负责数据完整性。然后,稍后当索引和约束重新打开时,数据库会告诉您是否有问题。【参考方案7】:我通常只在想要截断表时禁用外键约束,并且由于我不断回到这个答案,这是为未来的我准备的:
SET FOREIGN_KEY_CHECKS=0;
TRUNCATE TABLE table;
SET FOREIGN_KEY_CHECKS=1;
【讨论】:
【参考方案8】:要全局关闭外键约束,请执行以下操作:
SET GLOBAL FOREIGN_KEY_CHECKS=0;
完成后记得将其设置回来
SET GLOBAL FOREIGN_KEY_CHECKS=1;
警告:只有在进行单用户模式维护时才应该这样做。因为它可能导致数据不一致。例如,当您使用 mysqldump 输出上传大量数据时,它会非常有用。
【讨论】:
这是我需要知道的,所以这不是很好的做法,但是这家伙的回答应该得分更高...... 在尝试“最佳答案”对我不起作用后,这对我有用。也许可以添加对差异的解释。 @hexnet 不同的是SET FOREIGN_KEY_CHECKS
只是改变当前连接的值,而SET GLOBAL ..
改变所有连接的值>,包括未来的连接。如果您只是在一个窗口中执行SET FOREIGN..
,然后尝试在不同的窗口(通过不同的连接)应用该语句,则该值在那里没有改变。使用GLOBAL
,同一个变量对于两个连接具有相同的值。
播放更大的转储(6+ GB)时唯一可以帮助我
这对我不起作用。当我尝试时,我看到:ERROR 1228 (HY000): Variable 'foreign_key_checks' is a SESSION variable and can't be used with SET GLOBAL
【参考方案9】:
如果键字段可以为空,那么您也可以在尝试删除它之前将其值设置为空:
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()
【讨论】:
【参考方案10】:不要禁用约束,而是将其永久修改为 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)。
【讨论】:
注意更改表可能需要很长时间,最好将服务器全局设置为FOREIGN_KEY_CHECKS
为 0,并在完成脏活后将其放回原处。此外,它可能会因写表而锁定。
更改远程列类型时不会破坏引用吗? (看来我的客户将修改后的临时表重命名为原始表名。)以上是关于如何在 MySQL 中临时禁用外键约束?的主要内容,如果未能解决你的问题,请参考以下文章