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

Posted

技术标签:

【中文标题】如何在 MySQL 中临时禁用外键约束?【英文标题】:How can I temporarily disable a foreign key constraint in MySQL? 【发布时间】:2019-11-22 17:56:51 【问题描述】:

是否可以在 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】:

试试DISABLE KEYS

SET FOREIGN_KEY_CHECKS=0;

确保

SET FOREIGN_KEY_CHECKS=1;

之后。

【讨论】:

这是为整个 mysql 设置的还是只是那个会话? 我相信是每次会话。 serverfault.com/questions/291100/… ,还要注意你 cannot disable keys 代表 Innodb 我可以只为单个表禁用 FOREIGN_KEY_CHECKS 吗? 澄清@Pacerier 的评论:对于Innodb,您不能DISABLE KEYS。但是您可以使用显示的其他命令完成此问题中的要求:SET FOREIGN_KEY_CHECKS=0;(起初我以为他说没有办法做到这一点。)【参考方案2】:

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

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【参考方案3】:

我通常只在想要截断表时禁用外键约束,并且由于我不断回到这个答案,这是为未来的我准备的:

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

【讨论】:

【参考方案4】:

不要禁用约束,而是将其永久修改为 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,并在完成脏活后将其放回原处。此外,它可能会因写表而被锁定。 更改远程列类型时不会破坏引用吗? (看来我的客户将修改后的临时表重命名为原始表名。)【参考方案5】:

全局关闭外键约束:

SET GLOBAL FOREIGN_KEY_CHECKS = 0;

对于活动外键约束:

SET GLOBAL FOREIGN_KEY_CHECKS = 1;

【讨论】:

【参考方案6】:

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

在您的表中,转到 SQL 选项卡 编辑要运行的 SQL 命令后,GO 旁边有一个复选框,名为“启用外键检查”取消选中此复选框并运行您的 SQL。执行后会自动复查。

【讨论】:

谢谢!确实解决方案SET FOREIGN_KEY_CHECKS=0; ..... SET FOREIGN_KEY_CHECKS=1; 在 PHPMyAdmin 中对我不起作用,因为我忘记取消选中“启用外键检查”复选框。在 PHPMyAdmin 中,您可以跳过这些 SET 命令并取消选中复选框。【参考方案7】:

对我来说,仅仅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 它对我也不起作用【参考方案8】:

如果键字段可以为空,那么您也可以在尝试删除它之前将其值设置为空:

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()

【讨论】:

【参考方案9】:

在phpMyAdmin 中,您可以选择多行,然后可以单击删除操作。您将进入一个列出删除查询的屏幕。它看起来像这样:

请取消选中“启用外键检查”复选框,然后单击执行它们。

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

【讨论】:

【参考方案10】:

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

您创建外键是有原因的:因为子列中的所有值都应与父列中的值相同。如果没有外键约束,子行的值可能不在父行中,这会导致数据不准确。

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

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

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

【讨论】:

诚然,总会有取舍。 没有人说要永远这样运行它。您关闭约束,批量加载一些数据,然后将其重新打开。没什么大不了的,人们一直都在这样做。 批量导入是必须的,至少为了性能,很常见。有时您只需要恢复数据,然后您就可以进行检查。 这不是问题的答案。 注意,他的问题是暂时怎么做。这在进行某些维护和数据导入时是必需的。当然需要注意的是,您的导入脚本负责数据完整性。然后,稍后当索引和约束重新打开时,数据库会告诉您是否有问题。

以上是关于如何在 MySQL 中临时禁用外键约束?的主要内容,如果未能解决你的问题,请参考以下文章

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

如何在导入过程中禁用Oracle约束条件和触发器

如何在 Firebird 2.1 中临时禁用表中的所有约束?

mysql临时取消外键约束方法

Oracle中禁用了外键约束对系统有没有影响

MySQL禁用外键约束检查