mysql - 使用 mysql workbench : Error 1005: Can't create table (errno: 150) 创建 CHAR 类型的外键时出错

标签 mysql database-design mysql-workbench

我定义了以下 2 个表:

记录状态

显示创建表记录状态

CREATE TABLE `record_status` (  
  `record_status_id` int(10) unsigned NOT NULL AUTO_INCREMENT,  
  `status` char(6) NOT NULL,  
  `status_description` varchar(15) NOT NULL,  
  `created_at` datetime NOT NULL,  
  `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,  
  PRIMARY KEY (`record_status_id`,`status`)  
  ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=latin1

用户

显示创建表用户

CREATE TABLE `user` (  
  `user_id` int(10) unsigned NOT NULL AUTO_INCREMENT,  
  `handle` varchar(45) NOT NULL,  
  `email` varchar(255) NOT NULL,  
  `password` char(64) DEFAULT NULL,  
  `password_salt` binary(1) DEFAULT NULL,  
  `first_name` varchar(50) NOT NULL,  
  `last_name` varchar(50) NOT NULL,  
  `gender` char(1) DEFAULT NULL,  
  `birthday` date NOT NULL,  
  `created_at` datetime NOT NULL,  
  `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,  
  `user_status` char(6) DEFAULT NULL,  
  PRIMARY KEY (`user_id`),  
  KEY `usr_status_idx` (`user_status`)  
) ENGINE=InnoDB DEFAULT CHARSET=latin1

我尝试使用 mysql Workbench 添加 CHAR 类型的外键 user_status,如下所示:

ALTER TABLE `mydatabase`.`user`  
  ADD CONSTRAINT `usr_status`  
    FOREIGN KEY (`user_status`)  
    REFERENCES `mydatabase`.`record_status` (`status`)
    ON DELETE NO ACTION  
    ON UPDATE NO ACTION;

但我收到以下错误:

错误:

Executing SQL script in server
ERROR: Error 1005: Can't create table 'mydatabase.#sql-420_1b0' (errno: 150)

ALTER TABLE 'mydatabase'.'user'
ADD CONSTRAINT 'usr_status'
 FOREIGN KEY ('user_status')
 REFERENCES 'mydatabase'.'record_status'('status')
 ON DELETE NO ACTION
 ON UPDATE NO ACTION

SQL script execution finished: statements: 4 succeeded, 1 failed.

问题
我的目的是让状态列清楚地显示每个用户的当前状态(ACTIVE、INACTV、DELETD),同时仍然可以灵活地使用 record_status_id 将 record_status 表与用户表连接起来,以更好地查找具有给定状态的任何行性能。

我在这里找到了类似的帖子 Adding foreign key of type char in mysql 这建议更改我的主键排序规则但是,这将如何影响我的用户表?

我是否也必须更改用户表中 user_status 字段的排序规则?每次用户登录时都会查询用户表,我担心性能或这可能导致的任何限制。

我还打算将状态的外键添加到其他几个表中。我只想知道这对性能有何影响,或者它是否增加了任何限制?

任何关于我的设计的意见也将不胜感激。感谢您的帮助!

最佳答案

您遇到的问题实际上与排序规则无关(尽管排序规则可能是导致您在不同情况下遇到错误的原因)。

你的 FOREIGN KEY约束失败,因为您在 record_status.status单独没有索引.您将该列作为组合的一部分 PRIMARY KEY (record_status_id, status) ,但要成功创建外键约束,引用表和被引用表都必须完全在键关系中使用的列上有索引(除了相同的数据类型)。

添加 FOREIGN KEY约束在引用表上隐式创建必要的索引,但您仍然必须确保在引用表上有相应的索引。

鉴于您现在拥有的,如果您在 record_status.status 上添加一个索引,将正确创建约束。

CREATE TABLE `record_status` (  
  `record_status_id` int(10) unsigned NOT NULL AUTO_INCREMENT,  
  `status` char(6) NOT NULL,  
  `status_description` varchar(15) NOT NULL,  
  `created_at` datetime NOT NULL,  
  `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,  
  PRIMARY KEY (`record_status_id`,`status`),
  -- This would make your relationship work...
  KEY (`status`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=latin1

但是,我认为这不是最好的做法。我认为 (record_status_id, status) 上不需要复合主键,主要是因为 record_status_id本身就是AUTO_INCREMENT并保证是独一无二的。仅该列就可以是 PRIMARY KEY ,同时仍然添加额外的 UNIQUE KEYstatus以满足外键约束的索引要求。毕竟不是record_status_id组合status唯一标识每一行(制作主键)

CREATE TABLE `record_status` (  
  `record_status_id` int(10) unsigned NOT NULL AUTO_INCREMENT,  
  `status` char(6) NOT NULL,  
  `status_description` varchar(15) NOT NULL,  
  `created_at` datetime NOT NULL,  
  `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,  
  -- Primary only on record_status_id
  PRIMARY KEY (`record_status_id`),
  -- Additional UNIQUE index on status
  UNIQUE KEY (`status`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=latin1

关于设计——消除record_status_id ...

不知道您的应用程序的其余部分当前如何使用 record_status_id ,我不能确定您的应用程序代码是否需要它。但是,如果你想制作实际的 status值很容易被其他表使用,它只是 CHAR(6) ,您实际上可能不需要 record_status_id作为整数值。毕竟,如果 status string 旨在是唯一的,那么它完全能够充当 PRIMARY KEY独立,没有任何自动递增的整数键。

在这种情况下,您的 record_status表格如下所示,您的 FOREIGN KEY约束将正确添加到 users .

CREATE TABLE `record_status` (
  -- Remove the auto_increment column!!
  `status` char(6) NOT NULL,  
  `status_description` varchar(15) NOT NULL,  
  `created_at` datetime NOT NULL,  
  `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,  
  -- Status is unique, and therefore can be the PK on its own
  PRIMARY KEY (`status`)  
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=latin1

鉴于此设置,here's a sample显示成功创建表和添加 FK 约束。

您还询问了将状态 FK 添加到其他表的性能影响。在不知道目的的情况下很难推测,但如果其他表共享相同的 status值,那么创建它们的 FK 约束以链接到它是有意义的,就像你正在做的那样 users .如果是这种情况,我会建议以相同的方式进行操作,其中 status列是 CHAR(6) (或考虑将它们的全部 更改为 VARCHAR(6) )。 record_status.status 的值作为真正的主键仍然有意义,并且可以根据需要在尽可能多的相关表中用作 FK。

除了最巨大的规模之外,使用 INT 之间应该没有明显的性能差异。值和一个 CHAR(6)/VARCHAR(6)值作为外键。而且它们之间的存储大小差异同样很小。除非您必须将其扩展到非常大的比例,否则无需担心。

关于mysql - 使用 mysql workbench : Error 1005: Can't create table (errno: 150) 创建 CHAR 类型的外键时出错,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32402654/

相关文章:

php - 按时区从sql表中排序和选择

mysql - 带有 OneToOne 注释的数据库结构

mysql - 使用 SQL Server 创建 XML 文档

php - 在 PHP 中自动生成唯一 id,我可以指定 id 的起始编号

mysql - 在 MySql Workbench 中创建日期列时的默认值是多少?

MySQL 工作台 : Can't apply NOW() to timestamps field

mysql - 不支持的选项提供给 mysql_options()

c# - SubSonic DAL,找不到程序集?

mysql - 如何使用 MySql 使用一对多关系获取数据并在 MySql 中进行联接

amazon-web-services - 如何使用 MySQL Workbench 连接到私有(private)子网中的 RDS?