使用 mysql workbench 创建 CHAR 类型的外键时出错:错误 1005:无法创建 table (errno: 150)
Error when creating foreign key of type CHAR with mysql workbench: Error 1005: Can't create table (errno: 150)
我定义了以下2个table:
record_status
显示创建 TABLE record_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`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=latin1
用户
显示创建 TABLE 用户
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 table 与用户 table 使用record_status_id 以查找具有给定状态的任何行以获得更好的性能。
我在这里找到了一个类似的post
Adding foreign key of type char in mysql
这建议更改我的主键排序规则,但是,这将如何影响我的用户 table?
我是否也必须将排序规则更改为我的用户 table 中的 user_status 字段?每次用户登录时都会查询用户 table,我担心性能或这可能导致的任何限制。
我还打算为其他几个 table 的状态添加一个外键。我只想知道这对性能有何影响,或者它是否增加了任何限制?
任何关于我的设计的意见也将不胜感激。感谢您的帮助!
您遇到的问题实际上与整理无关(尽管整理可能是导致您在不同情况下遇到错误的原因)。
您的 FOREIGN KEY
约束失败,因为您在 record_status.status
上没有 单独 的索引。您将该列作为复合 PRIMARY KEY (record_status_id, status)
的一部分,但要成功创建外键约束,引用 table 和被引用 table 都必须在 上准确地具有索引键关系中使用的列(除了相同的数据类型)。
添加 FOREIGN KEY
约束会在引用 table 上隐式创建必要的索引,但您仍必须确保在引用的 table.[=45= 上有相应的索引]
鉴于您现在拥有的,如果您在 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
,同时仍然在 status
上添加额外的 UNIQUE KEY
以满足外键约束的索引要求。毕竟不是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
的情况下,我无法确定您的应用程序代码是否需要它。但是,如果您希望其他 table 可以轻松使用实际的 status
值,而它只是 CHAR(6)
,您实际上可能不需要 record_status_id
作为一个整数值。毕竟,如果 status
字符串是唯一的,那么它完全可以单独用作 PRIMARY KEY
,而无需任何自动递增整数键。
在这种情况下,您的 record_status
table 将如下所示,并且您的 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 显示成功创建了 table 并添加了 FK 约束。
您询问了将状态 FK 添加到其他 table 的性能影响。在不知道目的的情况下很难推测这一点,但如果其他 table 共享相同的 status
值,那么将它们的 FK 约束创建到 link 是有意义的假设您正在使用 users
。如果是这种情况,我建议以相同的方式进行操作,其中 status
列为 CHAR(6)
(或考虑将其中的 all 更改为 VARCHAR(6)
). record_status.status
的值作为真正的主键仍然有意义,并且可以根据需要在尽可能多的相关 table 中用作外键。
除了最庞大的规模之外,使用 INT
值和 CHAR(6)/VARCHAR(6)
值作为外键之间应该没有明显的性能差异。而且它们之间的存储大小差异同样很小。除非您必须将其扩展到非常大的比例,否则无需担心。
我定义了以下2个table:
record_status
显示创建 TABLE record_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`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=latin1
用户
显示创建 TABLE 用户
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 table 与用户 table 使用record_status_id 以查找具有给定状态的任何行以获得更好的性能。
我在这里找到了一个类似的post Adding foreign key of type char in mysql 这建议更改我的主键排序规则,但是,这将如何影响我的用户 table?
我是否也必须将排序规则更改为我的用户 table 中的 user_status 字段?每次用户登录时都会查询用户 table,我担心性能或这可能导致的任何限制。
我还打算为其他几个 table 的状态添加一个外键。我只想知道这对性能有何影响,或者它是否增加了任何限制?
任何关于我的设计的意见也将不胜感激。感谢您的帮助!
您遇到的问题实际上与整理无关(尽管整理可能是导致您在不同情况下遇到错误的原因)。
您的 FOREIGN KEY
约束失败,因为您在 record_status.status
上没有 单独 的索引。您将该列作为复合 PRIMARY KEY (record_status_id, status)
的一部分,但要成功创建外键约束,引用 table 和被引用 table 都必须在 上准确地具有索引键关系中使用的列(除了相同的数据类型)。
添加 FOREIGN KEY
约束会在引用 table 上隐式创建必要的索引,但您仍必须确保在引用的 table.[=45= 上有相应的索引]
鉴于您现在拥有的,如果您在 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
,同时仍然在 status
上添加额外的 UNIQUE KEY
以满足外键约束的索引要求。毕竟不是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
的情况下,我无法确定您的应用程序代码是否需要它。但是,如果您希望其他 table 可以轻松使用实际的 status
值,而它只是 CHAR(6)
,您实际上可能不需要 record_status_id
作为一个整数值。毕竟,如果 status
字符串是唯一的,那么它完全可以单独用作 PRIMARY KEY
,而无需任何自动递增整数键。
在这种情况下,您的 record_status
table 将如下所示,并且您的 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 显示成功创建了 table 并添加了 FK 约束。
您询问了将状态 FK 添加到其他 table 的性能影响。在不知道目的的情况下很难推测这一点,但如果其他 table 共享相同的 status
值,那么将它们的 FK 约束创建到 link 是有意义的假设您正在使用 users
。如果是这种情况,我建议以相同的方式进行操作,其中 status
列为 CHAR(6)
(或考虑将其中的 all 更改为 VARCHAR(6)
). record_status.status
的值作为真正的主键仍然有意义,并且可以根据需要在尽可能多的相关 table 中用作外键。
除了最庞大的规模之外,使用 INT
值和 CHAR(6)/VARCHAR(6)
值作为外键之间应该没有明显的性能差异。而且它们之间的存储大小差异同样很小。除非您必须将其扩展到非常大的比例,否则无需担心。