9

我有一个生产数据库,我在其中重命名了几个作为外键的列。显然,根据我的经验,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 损坏了我的数据库并销毁了它。我不得不从备份中重新加载才能重试。

帮助?:/

4

6 回答 6

9

两个表都是 InnoDB 类型吗?

company 表是否在 company_id 上有索引?

我猜您的表是 MyISAM(如果您没有更改配置,则为默认表)并且您无法在 MyISAM 中创建外键约束。请参阅您的两个表的 CREATE TABLE 的描述。

如果两个表都是空的,删除它们并重新创建它们,选择 InnoDB 作为引擎。您还可以在表创建脚本中添加 FOREIGN KEY 约束。


来自 MySQL参考手册

外键定义受以下条件约束:

  • 两个表都必须是 InnoDB表,并且不能是 TEMPORARY 表。

  • 外键和引用键中的对应列必须在 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;
于 2011-04-15T05:01:27.200 回答
4

确保 company_to_module.company_id 和 company.company_id 是完全相同的数据类型。当主键设置为 UNSIGNED INT 但外键字段只是一个 INT 时,我发生了这种情况。将 UNSIGNED 添加到数据类型解决了这个问题。

于 2011-04-15T04:54:30.623 回答
1

我只是使用 Windows 应用了重构,然后将数据库重新导入 Debian - 它可以工作。

我认为可以肯定地说 Debian 服务器或 linux 版本的 Mysql 出现了问题——可能是 5.1 版本中的错误?

反正我也把服务器上的内存从1gb升级到了2gb,这些问题都消失了。

我认为 MySQL 可能只是没有足够的内存来完成操作。如果是这样(而且似乎是这样),我认为 MySQL 应该简单地这么说而不是吐出这些错误 - 让我和这里的每个人都认为这是一个语法或与模式相关的问题。

无论如何,感谢那些试图提供帮助的人。至少它帮助我隔离了所有不可能的事情。

于 2011-04-16T20:11:13.357 回答
0

由于它似乎与语法无关,我最好的猜测是您创建 InnoDB 表的空间不足。

编辑:你能粘贴你的 InnoDB 配置吗:

SHOW VARIABLES LIKE "inno%";
于 2011-04-15T08:33:00.657 回答
0

由于尝试company_to_module手动创建副本会给您同样的错误,您应该仔细检查 .fk 中已经存在的 fk 约束company_to_module。它仍然有效,还是您修改了表格module

来自MySQL 文档

1005 (ER_CANT_CREATE_TABLE) 无法创建表。如果错误消息涉及错误 150,则表创建失败,因为未正确形成外键约束。

于 2011-04-15T10:31:14.627 回答
0

@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;
于 2011-04-15T10:45:15.917 回答