【问题标题】: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;

【问题讨论】:

    标签: mysql foreign-keys constraints truncate dml


    【解决方案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://stackoverflow.com/a/12085689/997776,这是查找孤立外键(恢复数据完整性)的好方法
    【解决方案2】:

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

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

    选项 1:

    1. 移除约束
    2. 执行TRUNCATE
    3. 手动删除现在引用 nowhere 的行
    4. 创建约束

    选项 2: user447951their answer

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

    【讨论】:

    • @barjonah:实际上,它可能会破坏数据完整性(请参阅stackoverflow.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。见这里:stackoverflow.com/questions/4767055/…
      【解决方案6】:

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

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

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

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

      【讨论】:

      • 关于 MySQL 的 TRUNCATE 不完整的注释 - 截断不应该调用触发器等。如果确实如此,它将与 DELETE 相同!它与行无关,因此无法执行与行相关的操作(如调用触发器或检查外键)。它在 OracleSql 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:不会有损害数据完整性的风险:

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

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

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

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

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

        重新创建外键:

        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
              

              【讨论】:

                猜你喜欢
                • 2019-11-04
                • 2016-07-28
                • 2019-11-30
                • 1970-01-01
                • 2011-04-20
                • 1970-01-01
                • 2020-12-12
                相关资源
                最近更新 更多