如何截断外键约束表?

Posted

技术标签:

【中文标题】如何截断外键约束表?【英文标题】:How to truncate a foreign key constrained table? 【发布时间】:2011-07-24 02:11:24 【问题描述】:

为什么mygroup 上的TRUNCATE 不起作用? 即使我有ON DELETE CASCADE SET 我得到:

错误 1701 (42000): 无法截断在外键约束中引用的表 (mytest.instance, CONSTRAINT instance_ibfk_1 FOREIGN KEY (GroupID) REFERENCES mytest.mygroup (@98765423) @))

drop database mytest;
create database mytest;
use mytest;

CREATE TABLE mygroup (
   ID    INT NOT NULL AUTO_INCREMENT PRIMARY KEY
) ENGINE=InnoDB;

CREATE TABLE instance (
   ID           INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
   GroupID      INT NOT NULL,
   DateTime     DATETIME DEFAULT NULL,

   FOREIGN KEY  (GroupID) REFERENCES mygroup(ID) ON DELETE CASCADE,
   UNIQUE(GroupID)
) ENGINE=InnoDB;

【问题讨论】:

【参考方案1】:

是的,你可以:

SET FOREIGN_KEY_CHECKS = 0;

TRUNCATE table1;
TRUNCATE table2;

SET FOREIGN_KEY_CHECKS = 1;

使用这些语句,您可能会将不遵守 FOREIGN KEY 约束的行放入表中。

【讨论】:

我们是否必须设置 SET FOREIGN_KEY_CHECKS=1;之后再来一次? 不,你没有。该设置仅在连接期间有效。断开连接后,下一次连接会将其设置回 1。 这不适用于引用表中的“ON DELETE”事件,因此这不是一个完整的答案。 如果在 phpMYADMIN 中使用,这仅在您使用同一 SQL 窗口中的所有事务时才有效(由 ; 分隔)。这是因为每个新的 Web SQL 调用都会将 FOREIGN_KEY_CHECKS 重置为 1。 如果您有兴趣http://***.com/a/12085689/997776,这是查找孤立外键(恢复数据完整性)的好方法【参考方案2】:

您不能 TRUNCATE 应用了 FK 约束的表(TRUNCATEDELETE 不同)。

要解决此问题,请使用以下任一解决方案。两者都存在破坏数据完整性的风险。

选项 1:

    移除约束 执行TRUNCATE 手动删除现在引用 nowhere 的行 创建约束

选项 2: user447951 在 their answer

中建议
SET FOREIGN_KEY_CHECKS = 0; 
TRUNCATE table $table_name; 
SET FOREIGN_KEY_CHECKS = 1;

【讨论】:

@barjonah:实际上,它可能会破坏数据完整性(请参阅***.com/questions/5452760/…)。所以,你在现实世界中所谓的“光”被认为是一种不好的做法。 PS:感谢投反对票 这里有一个很好的方法可以找到孤立的外键(恢复数据完整性)@9​​87654323@ @Dung 禁用外键检查只允许在开发阶段。它打破了任何现有的关系。 zerkms 在数据完整性方面是 100% 正确的。除非您打算完全清空它(或至少清空所有相关表),否则您不能在工作数据库上禁用外键检查 @Kamlesh 这不是我的解决方案,我建议不要这样做野蛮的方式。 @zerkms - 抱歉,如果我不清楚,当然 mysql 的 truncate 和 delete 实现是不同的。我要说的是 truncate 实现可能会更好 - 不要让它与 delete 语句相同。如果截断操作不违反 FK 约束(即子表为空),则没有理由错误地说它违反 FK - 因为不会有任何错误!这就是其他 RDBMS 截断实现的工作方式。【参考方案3】:

我会简单地这样做:

DELETE FROM mytest.instance;
ALTER TABLE mytest.instance AUTO_INCREMENT = 1;

【讨论】:

聪明。当你无论如何都想删除所有记录时,你不妨重置自动增量。 这显然是最好的方法。没有失去约束的风险,只是简单的删除。值得注意的是DELETE 的执行速度比TRUNCATE 慢。但由于这个动作通常很少执行,所以这无关紧要。 如果您只想这样做,这很好,但是如果您有太多行,DELETE 可能会非常残酷 - 因为它会命中日志,而 TRUNCATE 只是将数据撕掉。真的取决于用例。 我在使用delete语句时报错1175: You are using safe update mode, just add SET SQL_SAFE_UPDATES = 0;然后就好了 使用此方案时,报告error 1175: You are using safe update mode,...将删除子句更改为DELETE FROM mydb.mytable where id != 0使其完美。【参考方案4】:

如果您使用的是 phpMyAdmin,则很容易。

只需取消选中SQL 选项卡下的Enable foreign key checks 选项并运行TRUNCATE <TABLE_NAME>

【讨论】:

【参考方案5】:

你可以的

DELETE FROM `mytable` WHERE `id` > 0

【讨论】:

我试了一下bur,出现如下错误:Error Code: 1142. DELETE command denied to user 'root'@'localhost' for table 'mytable' 或者直接从mytable删除 这不会重置自动增量。 您当然可以做到这一点。但是,如果您有一张包含 700 万条记录的表,请在等待时去吃午饭。 @AAEM 可能有一些问题。通常意味着您需要向数据库上的“root”用户授予删除权限,或者运行flush privileges。见这里:***.com/questions/4767055/…【参考方案6】:

根据mysql documentation,TRUNCATE 不能用于具有外键关系的表。没有完全替代的 AFAIK。

删除约束仍然不会调用 ON DELETE 和 ON UPDATE。 我能 ATM 想到的唯一解决方案是:

删除所有行,删除外键,截断,重新创建键 删除所有行,重置 auto_increment(如果使用)

似乎 MySQL 中的 TRUNCATE 还不是一个完整的功能(它也不会调用触发器)。 见评论

【讨论】:

关于 MySQL 的 TRUNCATE 不完整的注释 - 截断不应该调用触发器等。如果确实如此,它将与 DELETE 相同!它与行无关,因此无法执行与行相关的操作(如调用触发器或检查外键)。它在 Oracle 和 Sql Server 中的工作方式相同。 为什么没有人提到 TRUNCATE 会重置 PRIMARY KEY?使用 DELETE 它不会重置 PRIMARY KEY,因此您的第一条记录将具有像 325579 这样的 ID,这很奇怪。 TRUNCATE 不应该以这种方式失败恕我直言。截断是重置表,所以无论如何它都应该重置。【参考方案7】:

在 MYSQL 数据库上测试

解决方案 1:

SET FOREIGN_KEY_CHECKS = 0;
TRUNCATE table1;

解决方案 2:

DELETE FROM table1;
ALTER TABLE table1 AUTO_INCREMENT = 1;
TRUNCATE table1;

这对我有用。我希望,这也会对你有所帮助。感谢您提出这个问题。

【讨论】:

解决方案 2 在 phpMyAdmin 中为我工作,但没有 TRUNCATE table1; 解决方案取决于 mysql 版本,这就是我发布 2 个解决方案的原因。乐于分享和帮助:) @Novasol 是的,你是对的,它可以工作,但如果你运行 TRUNCATE table1;命令然后mysql表索引将在内部自动设置为0,当您将此表1与任何其他表/表连接时将花费更少的时间。【参考方案8】:

虽然有人问过这个问题,但我不知道,但现在如果您使用 phpMyAdmin,您只需打开数据库并选择要截断的表即可。

在底部有一个包含许多选项的下拉菜单。打开它并选择标题Delete data or table 下的Empty 选项。 它会自动将您带到下一页,其中复选框中有一个名为 Enable foreign key checks 的选项。只需取消选择它并按下Yes 按钮,选定的表格就会被截断。

也许它在内部运行user447951's answer中建议的查询,但是从phpMyAdmin界面使用非常方便。

【讨论】:

【参考方案9】:

答案确实是the one provided by zerkms,如选项1所述:

选项 1:不会有损害数据完整性的风险:

    移除限制 执行截断 手动删除现在无处引用的行 创建约束

棘手的部分是移除约束,所以我想告诉你如何,以防有人需要知道如何做到这一点:

    运行SHOW CREATE TABLE <Table Name>查询以查看您的FOREIGN KEY的名称是什么(下图中的红框):

    运行ALTER TABLE <Table Name> DROP FOREIGN KEY <Foreign Key Name>。这将删除外键约束。

    删除相关的索引(通过表结构页面),你就完成了。

重新创建外键:

ALTER TABLE <Table Name>
ADD FOREIGN KEY (<Field Name>) REFERENCES <Foreign Table Name>(<Field Name>);

【讨论】:

【参考方案10】:

只需使用 CASCADE

TRUNCATE "products" RESTART IDENTITY CASCADE;

但要为级联删除做好准备)

【讨论】:

OP 标记了 MySQL。虽然这在 Postgres 中有效,但在 MySQL 中不正确。【参考方案11】:

获取旧的外键检查状态和 sql 模式是在将模型同步到数据库时像 Mysql Workbench 那样截断/删除表的最佳方法。

SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0;
SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;`
SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='TRADITIONAL,ALLOW_INVALID_DATES';

DROP TABLE TABLE_NAME;
TRUNCATE TABLE_NAME;

SET SQL_MODE=@OLD_SQL_MODE;
SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;
SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;

【讨论】:

【参考方案12】:

如果表的数据库引擎不同,您将收到此错误,因此请将它们更改为 InnoDB

ALTER TABLE my_table ENGINE = InnoDB;

【讨论】:

【参考方案13】:

另一种解决方法是删除表中的所有行,然后重置自动增量列:

delete from table_name where 1

然后运行:

ALTER TABLE table_name AUTO_INCREMENT = 1

【讨论】:

以上是关于如何截断外键约束表?的主要内容,如果未能解决你的问题,请参考以下文章

使用truncate ,截断有外键约束的父表

SequelizeDatabaseError:无法截断外键约束中引用的表

暂时禁用所有外键约束

怎么添加外键约束

SQL中如何为表添加外键约束

markdown 在Laravel 5.7种子文件中截断具有外键约束的表。