我定义了以下 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 KEY
在 status
以满足外键约束的索引要求。毕竟不是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/