使用 Alter Table 和现有 MYSQL 数据库添加外键时出现问题 - 无法添加!帮助!
Posted
技术标签:
【中文标题】使用 Alter Table 和现有 MYSQL 数据库添加外键时出现问题 - 无法添加!帮助!【英文标题】:Problem adding Foreign Key using Alter Table with existing MYSQL Database - can't add it! Help! 【发布时间】:2011-08-06 01:26:55 【问题描述】:我有一个生产数据库,我在其中重命名了几个作为外键的列。显然,根据我的经验,mysql 让这件事变得非常痛苦。
我的解决方案是删除所有索引和外键,重命名 id 列,然后重新添加索引和外键。
这在用于开发数据库的 windows 上的 mysql 5.1 上效果很好。
我去我的 debian 服务器上运行我的迁移脚本,它也使用 mysql 5.1,它给出了以下错误:
mysql> ALTER TABLE `company_to_module`
-> ADD CONSTRAINT `FK82977604FE40A062` FOREIGN KEY (`company_id`) REFERENCES `company` (`company_id`) ON DELETE RESTRICT ON UPDATE RESTRICT;
ERROR 1005 (HY000): Can't create table 'jobprep_production.#sql-44a5_76' (errno: 150)
此表中没有与我尝试添加的外键发生冲突的值。数据库没有改变。外键 DID 之前存在......所以数据很好。更不用说我在服务器上使用了相同的数据库,它在 Windows 上迁移得很好。但是这些相同的外键迁移并没有出现在 Debian 上。
这些列使用相同的类型 - BIGINT (20)
这些名称确实存在于它们各自的表中。
这些表是 innodb。他们已经在其他列中有外键。这不是一个新的数据库。
我无法删除表,因为这是一个生产数据库。
我的数据库中的“原样”表:
CREATE TABLE `company_to_module` (
`company_id` bigint(20) NOT NULL,
`module_id` bigint(20) NOT NULL,
KEY `FK8297760442C8F876` (`module_id`),
KEY `FK82977604FE40A062` (`company_id`) USING BTREE,
CONSTRAINT `FK8297760442C8F876` FOREIGN KEY (`module_id`) REFERENCES `module` (`module_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
和
Create Table: CREATE TABLE `company` (
`company_id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`address` varchar(255) DEFAULT NULL,
`postal_code` varchar(255) DEFAULT NULL,
`province_id` bigint(20) DEFAULT NULL,
`phone_number` varchar(255) DEFAULT NULL,
`is_enabled` bit(1) DEFAULT NULL,
`director_id` bigint(20) DEFAULT NULL,
`homepage_viewable` bit(1) NOT NULL DEFAULT b'1',
`courses_created` int(10) NOT NULL DEFAULT '0',
`header_background` varchar(25) DEFAULT '#172636',
`display_name` varchar(25) DEFAULT '#ffffff',
`tab_background` varchar(25) DEFAULT '#284767',
`tab_text` varchar(25) DEFAULT '#ffffff',
`hover_tab_background` varchar(25) DEFAULT '#284767',
`hover_tab_text` varchar(25) DEFAULT '#f2e0bd',
`selected_tab_background` varchar(25) DEFAULT '#f5f5f5',
`selected_tab_text` varchar(25) DEFAULT '#172636',
`hover_table_row_background` varchar(25) DEFAULT '#c0d2e4',
`link` varchar(25) DEFAULT '#4e6c92',
PRIMARY KEY (`company_id`),
KEY `FK61AE555A71DF3E03` (`province_id`),
KEY `FK61AE555AAC50C977` (`director_id`),
CONSTRAINT `company_ibfk_1` FOREIGN KEY (`director_id`) REFERENCES `user_account` (`user_account_id`),
CONSTRAINT `FK61AE555A71DF3E03` FOREIGN KEY (`province_id`) REFERENCES `province` (`province_id`)
) ENGINE=InnoDB AUTO_INCREMENT=24 DEFAULT CHARSET=utf8
这里是 innodb 状态:
------------------------
LATEST FOREIGN KEY ERROR
------------------------
110415 3:14:34 Error in foreign key constraint of table jobprep_production/#sql-44a5_1bc:
FOREIGN KEY (`company_id`) REFERENCES `company` (`company_id`) ON DELETE RESTRICT ON UPDATE RESTRICT:
Cannot resolve column name close to:
) ON DELETE RESTRICT ON UPDATE RESTRICT
如果我尝试从“company_to_module”中删除索引,我会收到以下错误:
#1025 - Error on rename of './jobprep_production/#sql-44a5_23a' to './jobprep_production/company_to_module' (errno: 150)
这是我的 innodb 变量:
+---------------------------------+------------------------+
| Variable_name | Value |
+---------------------------------+------------------------+
| innodb_adaptive_hash_index | ON |
| innodb_additional_mem_pool_size | 1048576 |
| innodb_autoextend_increment | 8 |
| innodb_autoinc_lock_mode | 1 |
| innodb_buffer_pool_size | 8388608 |
| innodb_checksums | ON |
| innodb_commit_concurrency | 0 |
| innodb_concurrency_tickets | 500 |
| innodb_data_file_path | ibdata1:10M:autoextend |
| innodb_data_home_dir | |
| innodb_doublewrite | ON |
| innodb_fast_shutdown | 1 |
| innodb_file_io_threads | 4 |
| innodb_file_per_table | OFF |
| innodb_flush_log_at_trx_commit | 1 |
| innodb_flush_method | |
| innodb_force_recovery | 0 |
| innodb_lock_wait_timeout | 50 |
| innodb_locks_unsafe_for_binlog | OFF |
| innodb_log_buffer_size | 1048576 |
| innodb_log_file_size | 5242880 |
| innodb_log_files_in_group | 2 |
| innodb_log_group_home_dir | ./ |
| innodb_max_dirty_pages_pct | 90 |
| innodb_max_purge_lag | 0 |
| innodb_mirrored_log_groups | 1 |
| innodb_open_files | 300 |
| innodb_rollback_on_timeout | OFF |
| innodb_stats_on_metadata | ON |
| innodb_support_xa | ON |
| innodb_sync_spin_loops | 20 |
| innodb_table_locks | ON |
| innodb_thread_concurrency | 8 |
| innodb_thread_sleep_delay | 10000 |
+---------------------------------+------------------------+
我还想补充一点,当我在添加外键时,mysql 损坏了我的数据库并破坏了它。我必须从备份重新加载才能重试。
帮助? :/
【问题讨论】:
两个表都是InnoDB
类型吗?
company
表是否在company_id
上有索引?
我猜你的表是MyISAM
(如果你没有改变配置的默认值),我认为你不能在 MyISAM 中创建外键约束。查看CREATE TABLE company_to_module
的描述。
如果两个表都是空的,删除它们并重新创建它们,选择 InnoDB
作为引擎。您还可以在表创建脚本中添加FOREIGN KEY
约束。
@Ypercube:感谢您的提示。两个表都不是空的 - 这是一个生产数据库。我必须用户更改表。所有的表都是innodb。事实上,这些表上已经存在外键——它们并不是第一个。该数据库有 40 个表,所有表中都有大量外键。所以我不认为这是任何问题 :( 是的,公司的 id 是“company_id”。我肯定会这样做。尽管没有外键,但该应用程序实际上可以与数据库一起使用......但我只是无论如何都想添加它们。
【参考方案1】:
两个表都是 InnoDB 类型的吗?
company 表是否有关于 company_id 的索引?
我猜您的表是 MyISAM(如果您没有更改配置,则为默认表)并且您无法在 MyISAM 中创建外键约束。请参阅您的两个表的 CREATE TABLE 说明。
如果两个表都是空的,则删除它们并重新创建它们,选择 InnoDB 作为引擎。您还可以在表创建脚本中添加 FOREIGN KEY 约束。
来自 MySQL Reference Manual:
外键定义需满足以下条件:
两个表都必须是 InnoDB 表和 它们不能是 TEMPORARY 表。
对应栏目在国外 key 和引用的 key 必须有 类似的内部数据类型里面 InnoDB 以便它们可以进行比较 没有类型转换。 尺寸 整数类型的符号必须是 一样的。字符串类型需要的长度 不一样。 对于非二进制 (字符)字符串列, 字符集和排序规则必须是 一样的。
InnoDB 需要外部索引 键 和引用的键,以便 外键检查可以快速且 不需要表扫描。在里面 引用表,必须有一个 外键列的索引 被列为第一列 相同的顺序。 这样的索引是 在引用表上创建 如果不存在则自动。 (这与一些旧的 版本,其中索引必须是 显式创建或创建 外键约束会失败。) index_name,如果给定,用作 前面已经介绍过了。
InnoDB 允许外键 引用任何索引列或组 的列。然而,在 引用的表,必须有一个 引用列的索引 被列为第一列 相同的顺序。
外键列的索引前缀 不支持。后果之一 这是 BLOB 和 TEXT 列 不能包含在外键中 因为这些列上的索引必须 始终包含前缀长度。
如果 CONSTRAINT 符号子句是 给定,符号值必须是 在数据库中是唯一的。如果从句 未给出,InnoDB 创建名称 自动。
@egervari:如果你运行这个会发生什么:
CREATE TABLE `test` (
`company_id` bigint(20) NOT NULL,
`module_id` bigint(20) NOT NULL,
KEY (`module_id`),
KEY (`company_id`),
CONSTRAINT `test_fk_module`
FOREIGN KEY (`module_id`)
REFERENCES `module` (`module_id`),
CONSTRAINT `test_fk_company`
FOREIGN KEY (`company_id`)
REFERENCES `company` (`company_id`)
ON DELETE RESTRICT
ON UPDATE RESTRICT
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;
如果你运行:
ALTER TABLE `company_to_module`
ADD CONSTRAINT `company_to_module_fk_company`
FOREIGN KEY (`company_id`)
REFERENCES `company` (`company_id`)
ON DELETE RESTRICT
ON UPDATE RESTRICT;
【讨论】:
是的。我正在做所有这些事情。这些表是 Innodb。 id 命名正确。数据是正确的。类型是正确的。问题:假设我确实违反了这些规则......为什么这些迁移会在使用完全相同的数据库的 Windows 上工作?这是最莫名其妙的事情。 内存不足可能是导致此错误的原因吗?这只是一个云服务器,“免费”通常说我有 29-9 兆的免费 1 演出。只是好奇:/ 很多用于缓冲区,所以我不确定这意味着什么。 @egervari: 当你运行上面的CREATE TABLE
时你会得到类似的信息吗?
@egervari:如果你运行ALTER TABLE
?
它在 debian mysql 上不起作用(我显然之前尝试过......这就是我想要做的),但是你的 alter table 在 windows 数据库上工作(相同的 .sql 文件) 就像我的一样。【参考方案2】:
确保 company_to_module.company_id 和 company.company_id 是完全相同的数据类型。当主键设置为 UNSIGNED INT 但外键字段只是一个 INT 时,我发生了这种情况。将 UNSIGNED 添加到数据类型可以解决问题。
【讨论】:
值得一试。我所有的 id 都是 BIGINT (20) :( 我只是要粘贴我的表定义 为我工作 - 我有一个 unsigned int 和另一个我试图链接的 int。谢谢!【参考方案3】:我只是使用 Windows 应用了重构,然后将数据库重新导入 Debian - 它可以工作。
我认为可以肯定地说 Debian 服务器或 linux 版本的 Mysql 出现了问题 - 可能是 5.1 版本中的错误?
不管怎样,我也把服务器上的内存从1gb升级到2gb,这些问题都消失了。
我认为 MySQL 可能只是没有足够的内存来完成操作。如果是这样(而且似乎是这样),我认为 MySQL 应该简单地说出来而不是吐出这些错误 - 让我和这里的每个人都认为这是一个语法或架构相关的问题。
无论如何,感谢那些试图提供帮助的人。至少它帮助我隔离了所有不可能的事情。
【讨论】:
【参考方案4】:由于它似乎与语法无关,我最好的猜测是您创建 InnoDB 表的空间不足。
编辑:你能粘贴你的 InnoDB 配置吗:
SHOW VARIABLES LIKE "inno%";
【讨论】:
"innodb_data_file_path" 由于某种原因实际上根本不在 my.cnf 中。 “innodb_file_per_table”也不是。我个人没有对 my.cnf 做任何事情——这些是默认设置。这可能是问题吗?默认值真的很低吗? 这是 ibdata1 上的统计数据:“35651584 Apr 15 03:20 ibdata1” 我粘贴了上面的变量;)【参考方案5】:由于尝试手动创建company_to_module
的副本会给您同样的错误,您应该仔细检查company_to_module
中已经存在的fk 约束。还有效吗,还是修改了表module
?
来自MySQL-Docs:
1005 (ER_CANT_CREATE_TABLE) 无法创建表。如果错误消息涉及错误 150,则表创建失败,因为没有正确形成外键约束。
【讨论】:
它给出了错误,因为我忘记在 company_to_module 的重复表定义中更改外键名称。一旦我意识到,创建表就可以了——这意味着创建新表不是问题。但是,删除“module_id”的索引会导致我在上面写的新错误。老实说,我已经厌倦了这种“猜测工作”,我只是导出了我的数据库,在 Windows 上添加了外键(是的,它们工作!),然后将数据库重新导入 debian。有用。我不知道为什么它可以在 Windows 而不是 linux 上运行。交叉兼容性就这么多了。【参考方案6】:@egervari 您写的 - 我的解决方案是删除所有索引和外键,重命名 id 列,然后重新添加索引和外键。
同意你的看法。但这可能是出了什么问题。我重现了错误,并(在我的情况下)修复了它。
我建议您对重命名列的表运行 OPTIMIZE TABLE 命令。文档说 - 对于 InnoDB 表,OPTIMIZE TABLE 映射到 ALTER TABLE,它会重建表以更新索引统计信息并释放聚集索引中未使用的空间。
另一种解决方案:
删除引用表中的唯一键(外键使用的键,在您的情况下它是主键)。然后添加新的外键并重新创建丢弃的唯一键。
另一种解决方案:
尝试向引用的表添加和删除新列,然后尝试创建外键。
ALTER TABLE company ADD COLUMN column1 VARCHAR(255) DEFAULT NULL;
ALTER TABLE company DROP COLUMN column1;
【讨论】:
虽然我从来没有提到过,但我确实对所有内容进行了优化。不幸的是,这也没有用。 我不能放下任何钥匙。这是一个生产数据库。 ://以上是关于使用 Alter Table 和现有 MYSQL 数据库添加外键时出现问题 - 无法添加!帮助!的主要内容,如果未能解决你的问题,请参考以下文章
MySQL的表现ALTER TABLE在列之后添加列 - 在一张大桌子上
sql [ALTER TABLE ADD COLUMN]将新列添加到现有表#SQL