重命名InnoDB表而不更新外键引用?

Bar*_*sen 32 mysql innodb

我试图用新表替换InnoDB表,我希望所有指向旧表的外键引用指向新表.

所以我尝试了这个:

SET foreign_key_checks = 0;
ALTER TABLE foo RENAME foo_old;
ALTER TABLE foo_new RENAME foo;
Run Code Online (Sandbox Code Playgroud)

不幸的是,即使禁用了foreign_key_checks,指向foo的所有引用都会更改为指向foo_old.现在我正在寻找

  • 一种在不重建整个表的情况下更改外键引用的方法,或者
  • 一种重命名表而不更新外键引用的方法.

我尝试删除外键并重新创建它们,但由于表格很大,需要几个小时.替换表的重点是在有限的停机时间内进行模式更改.

xel*_*ber 11

旧问题,但以下是可能的方法.基本上移动数据而不是重命名表.您当然需要确保新数据符合外键规则.

SET foreign_key_checks = 0;
CREATE TABLE IF NOT EXISTS foo_old LIKE foo;
INSERT INTO foo_old SELECT * FROM foo;
TRUNCATE foo;
INSERT INTO foo SELECT * FROM foo_new;
Run Code Online (Sandbox Code Playgroud)

确保将其作为一个查询运行,以便foreign_key_checks适用于整个事务.希望这可以帮助.

  • 如果表有数百万条记录并且时间很短,那么这是不可行的.(想想第一个副本需要两个小时,有时需要截断两个小时,还需要两个小时才能复制.) (6认同)

Jam*_*s C 7

不幸的是,我不认为有一种方法可以解决你的问题而不先删除外键并重新创建它们.

这是次要的,但我也发现了你的RENAME命令.您可以将它们链接在一起,除非所有步骤都成功,否则它将回滚所有其他重命名.这是语法:

RENAME TABLE foo TO foo_old, foo_new TO foo;
Run Code Online (Sandbox Code Playgroud)

  • 我注意到丢弃foo然后重新创建它可以保持外键不变.由于foo_new基本上是foo的副本(加上额外内容)(使用SELECT INTO OUTFILE然后使用LOAD DATA INFILE创建),这可能是一个解决方案.我只是删除表,重新创建它,然后将数据加载到其中.只有重命名,我才喜欢这样一个事实,即当出现问题时我有一个"备份"表.并且似乎向后移动表使得引用保持不变(即使它们然后指向不存在的表)但是在不更改引用的情况下重命名表是不可能的. (5认同)
  • @JamesC,`RENAME`将为您更改外键.即如果`bar`引用`foo`并且你将`foo`重命名为`foo_old`,那么`bar`上的所有外键现在将引用`foo_old`.链接重命名为单个语句不会改变这一点. (2认同)

vhu*_*vhu 7

在MySQL 5.6上innodb_file_per_table=ON允许您动态交换表空间.这不能完全使用SQL来完成,因为文件操作需要单独执行.首先准备foo_new要复制的表并删除foo数据:

SET foreign_key_checks = 0;
ALTER TABLE foo DISCARD TABLESPACE;
FLUSH TABLES foo_new FOR EXPORT;
Run Code Online (Sandbox Code Playgroud)

此时,您需要将相关的InnoDB文件复制到正确的名称.文件存储在数据目录中.Debian的,例如,它们在默认情况下/var/lib/mysql/yourdatabase和文件foo_new.ibd,foo_new.cfg以及foo_new.frm.将它们分别复制到foo.ibd,foo.cfgfoo.frm.例如:

$ cp foo_new.ibd foo.ibd
$ cp foo_new.frm foo.frm
$ cp foo_new.cfg foo.cfg
Run Code Online (Sandbox Code Playgroud)

注意MySQL可以访问新文件(例如,他们拥有正确的所有者,访问权限).完成后,您可以再次导入表并启用外键:

UNLOCK TABLES;
ALTER TABLE foo IMPORT TABLESPACE;
SET foreign_key_checks = 1;
Run Code Online (Sandbox Code Playgroud)

这只复制foo_newfoo.如果需要复制foo,请重复这些步骤foo_old.


Ada*_*dam 5

InnoDB在外键中使用表的内部指针,因此无论给该表提供什么名称(使用RENAME),约束都将保留,包括使用时SET foreign_key_checks = 0

无需重新构建整个表即可更改外键引用的方法

使用innodb_file_per_table=ON将是我们可以使用的最接近的(请参阅@vhu答案)。

一种在不更新外键引用的情况下重命名表的方法。

如果数据变化不大,该解决方案将意味着最短的停机时间和工作量,并且不需要外壳访问服务器,可能只是使用两个数据库,并在适当的时间打开它们。

同步所有其他表可能比在应用程序中同步大表或临时复制mysql命令(删除,更新,插入)要快得多,直到您进行了一些更改为止。