简体   繁体   中英

Error when creating foreign key of type CHAR with mysql workbench: Error 1005: Can't create table (errno: 150)

I have defined the following 2 tables:

record_status

SHOW CREATE 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

user

SHOW CREATE TABLE user

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

and I tried adding the foreign key user_status of type CHAR using mysql Workbench as follows:

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;

but I am getting the following error:

Error :

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.

Question
My intention is to have the status column clearly show the current status for each user (ACTIVE, INACTV, DELETD) while still having the flexibility to join the record_status table with the user table using the record_status_id to find any rows with a given status for better performance.

I found a similar post here Adding foreign key of type char in mysql which suggests to change my primary key's collation but, how would that affect my user table?

Will I have to change the collation to the user_status field in my user table as well? The user table will be queried every time a user logs in and I am concerned about performance or any constraints this may cause.

I also intend to add a foreign key for the status to a few other tables as well. I would just like to know how this affects performance, or does it add any constraints?

Any input regarding my design will also be appreciated. Thank you for your help!

The issue you're facing isn't actually related to collation (though collation can be a cause of the error you're experiencing under different circumstances).

Your FOREIGN KEY constraint is failing because you don't have an index individually on record_status.status . You have that column as part of the composite PRIMARY KEY (record_status_id, status) , but for successful foreign key constraint creation, both the referencing table and the referenced table must have indexes on exactly the columns used in the key relationship (in addition to the same data types).

Adding the FOREIGN KEY constraint implicitly creates the necessary index on the referencing table, but you must still ensure you have the corresponding index on the referenced table.

So given what you have now, if you added a single index on record_status.status , the constraint would correctly be created.

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

However, I don't think that's the best course of action. I don't see a need for the composite primary key on (record_status_id, status) , chiefly because the record_status_id is itself AUTO_INCREMENT and guaranteed to be unique. That column alone could be the PRIMARY KEY , while still adding an additional UNIQUE KEY on status to satisfy the foreign key constraint's indexing requirement. After all, it is not the combination of record_status_id and status which uniquely identifies each row (making a primary key)

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

About the design -- eliminating record_status_id ...

Without knowing how the rest of your application currently uses record_status_id , I can't say for sure if it required by your application code. But, if you wish to make the actual status value easily available to other tables, and it is merely CHAR(6) , it is possible that you actually have no need for record_status_id as an integer value. After all, if the status string is intended to be unique, then it is perfectly capable of serving as the PRIMARY KEY on its own, without any auto-increment integer key.

In that case, your record_status table would look like below, and your FOREIGN KEY constraint would correctly be added to 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

Given this setup, here's a sample showing the successful creation of the tables and addition of the FK constraint.

You asked about performance implications of adding a status FK to other tables as well. It's tough to speculate on that without knowing the purpose, but if other tables share the same status values, then it makes sense to create their FK constraints to link to it in the same say you're doing with users . And if that's the case, I would recommend doing it the same way, wherein the status column is CHAR(6) (or consider changing all of them to VARCHAR(6) ). The value of record_status.status still makes sense as the true primary key, and can be used as the FK in as many related tables as necessary.

In all but the most gigantic scale, there should be no appreciable performance difference between using an INT value and a CHAR(6)/VARCHAR(6) value as the foreign key. And the storage size difference between them is equally tiny. It isn't worth worrying about unless you must scale this to positively enormous proportions.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM