MySQL Error #1215 - Cannot Add Foreign Key Constraint - mysql

I have the following problem with a pretty simple MySQL database table creation:
SQL query:
CREATE TABLE IF NOT EXISTS comments(
commentId INT NOT NULL AUTO_INCREMENT,
comment VARCHAR(1024) NOT NULL,
commentdate TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
profileId INT NOT NULL,
PRIMARY KEY(commentId),
CONSTRAINT fk_ProfilesProfileId FOREIGN KEY(profileId) REFERENCES profiles.profileId ON UPDATE RESTRICT ON DELETE CASCADE
)Engine=InnoDB DEFAULT CHARSET utf8 COLLATE utf8_general_ci
Error:
MySQL said: Documentation
#1215 - Cannot add foreign key constraint
As you can see, the error number is 1215, which would indicate that the problem is about Foreign Key and Primary Key in comments and profiles tables (respectively) being different types. However, I have checked multiple times that they both are INT(11) and signed.
Here are queries for both of them:
'profiles' -table
CREATE TABLE IF NOT EXISTS profiles(
profileId INT NOT NULL AUTO_INCREMENT,
corpname VARCHAR(512) NOT NULL DEFAULT 'VIRHE',
corpserial VARCHAR(16) NOT NULL DEFAULT 'VIRHE',
loginusername VARCHAR(128) NOT NULL,
loginpassword VARCHAR(128) NOT NULL,
profilephone VARCHAR(16),
mapsphone VARCHAR(16),
added TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
done TIMESTAMP DEFAULT NULL,
coderequest TIMESTAMP DEFAULT NULL,
confirmed TIMESTAMP DEFAULT NULL,
PRIMARY KEY(profileId),
INDEX corpnameIndex (corpname),
INDEX corpserialIndex (corpserial)
)Engine=InnoDB DEFAULT CHARSET utf8 COLLATE utf8_general_ci;
'comments' -table
CREATE TABLE IF NOT EXISTS comments(
commentId INT NOT NULL AUTO_INCREMENT,
comment VARCHAR(1024) NOT NULL,
commentdate TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
profileId INT NOT NULL,
PRIMARY KEY(commentId),
CONSTRAINT fk_ProfilesProfileId FOREIGN KEY(profileId) REFERENCES profiles.profileId ON UPDATE RESTRICT ON DELETE CASCADE
)Engine=InnoDB DEFAULT CHARSET utf8 COLLATE utf8_general_ci;
It is possible that I am dumb, and I'm just missing something. Hopefully it's so.

I replicated your example locally. It seems that the problem is how you declare your constraint for the foreign key.
CREATE TABLE profiles (
profileId int(11) NOT NULL AUTO_INCREMENT,
corpname varchar(512) NOT NULL DEFAULT 'VIRHE',
corpserial varchar(16) NOT NULL DEFAULT 'VIRHE',
loginusername varchar(128) NOT NULL,
loginpassword varchar(128) NOT NULL,
profilephone varchar(16) DEFAULT NULL,
mapsphone varchar(16) DEFAULT NULL,
added timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
done timestamp NULL DEFAULT NULL,
coderequest timestamp NULL DEFAULT NULL,
confirmed timestamp NULL DEFAULT NULL,
PRIMARY KEY (profileId),
KEY corpnameIndex (corpname(255)),
KEY corpserialIndex (corpserial)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS comments(
commentId INT NOT NULL AUTO_INCREMENT,
comment VARCHAR(1024) NOT NULL,
commentdate TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
profileId INT NOT NULL,
PRIMARY KEY(commentId),
CONSTRAINT fk_ProfilesProfileId FOREIGN KEY(profileId) REFERENCES profiles(profileId) ON UPDATE RESTRICT ON DELETE CASCADE
)Engine=InnoDB DEFAULT CHARSET utf8 COLLATE utf8_general_ci;
with respect to your code:
The syntax for the foreign key after REFERENCES is table(field) rather than table.field
TIMESTAMP DEFAULT NULL should be TIMESTAMP NULL.
Note the partial index corpnameIndex, some considerations follow hereunder.
Some information about indexes the engine you want to use is InnoDB, which specifies that (from here):
By default, an index key for a single-column index can be up to 767
bytes. The same length limit applies to any index key prefix. See
Section 13.1.13, “CREATE INDEX Syntax”. For example, you might hit
this limit with a column prefix index of more than 255 characters on a
TEXT or VARCHAR column, assuming a UTF-8 character set and the maximum
of 3 bytes for each character. When the innodb_large_prefix
configuration option is enabled, this length limit is raised to 3072
bytes, for InnoDB tables that use the DYNAMIC and COMPRESSED row
formats.
The problem in your table is INDEX corpnameIndex (corpname), because this index would be 512*3 = 1536 bytes long.
You have some options here:
Change corpname size to 255 by declaring it as follows corpname VARCHAR(255) NOT NULL DEFAULT 'VIRHE'
Enable innodb_large_prefix on your mysql by running set global innodb_large_prefix = ON;. This will bring the limit to 3072 bytes (1024 chars).
Index only a part of your corpname field by using ADD INDEX (corpname(255))

Related

Create a relationship between the tables when the table is first created

I use mysql
in my database
I want to create multiple tables with delete and update relationship
as an example
I want to link the profiles table to the users table
Associate the mm_id_user column from the users table with the mm_id_profile column from the profiles table
What I actually tried to do is this example and it didn't work for me
https://stackoverflow.com/a/260453/10206991
https://stackoverflow.com/a/9796950/10206991
The code used to create the users table and it succeeds in creating the table
CREATE TABLE users (
mm_email VARCHAR(255) NOT NULL,
mm_id_user int NOT NULL AUTO_INCREMENT,
mm_name VARCHAR(25) NOT NULL,
mm_password VARCHAR(255) NOT NULL,
mm_code_reset_pass VARCHAR(100) NOT NULL,
mm_code_check_email VARCHAR(100) NOT NULL,
mm_status CHAR(1) NOT NULL DEFAULT '0',
mm_date_create_account TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
mm_date_last_login TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
mm_is_login CHAR(1) NOT NULL DEFAULT '0',
PRIMARY KEY (mm_id_user, mm_email)
)ENGINE=InnoDB DEFAULT CHARSET=utf8 DEFAULT COLLATE utf8_unicode_ci;
The code used to create the profiles table. He failed to create the table
CREATE TABLE profiles (
mm_id_profile VARCHAR(255) NOT NULL,
mm_image_user TEXT NOT NULL,
mm_num_stores_all VARCHAR(4) NOT NULL,
mm_num_stores_exist VARCHAR(4) NOT NULL,
mm_name_public VARCHAR(25) NOT NULL,
mm_email_public VARCHAR(255) NOT NULL,
mm_phone_public VARCHAR(20) NOT NULL,
mm_location_public TEXT NOT NULL,
mm_update_date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
INDEX index_profiles(mm_name_public, mm_email_public),
PRIMARY KEY (mm_id_profile),
FOREIGN KEY (mm_id_profile) REFERENCES users(mm_email) ON DELETE CASCADE
)ENGINE=InnoDB DEFAULT CHARSET=utf8 DEFAULT COLLATE utf8_unicode_ci;
But if you remove the following line from the profiles table, it will be created
But there is definitely no relationship between the two tables
FOREIGN KEY (mm_id_profile) REFERENCES users(mm_email) ON DELETE CASCADE
This is the error message that appears when creating a profiles table
#1005 - Can't create table `mustforu_test`.`profiles` (errno: 150 "Foreign key constraint is incorrectly formed") (Details…)
There's no reason to put both mm_id_user and mm_email in the primary key of users. If you want emails to be unique, it should have its own unique index. Your current primary key would allow duplicate emails with different IDs.
CREATE TABLE users (
mm_email VARCHAR(255) NOT NULL,
mm_id_user int NOT NULL AUTO_INCREMENT,
mm_name VARCHAR(25) NOT NULL,
mm_password VARCHAR(255) NOT NULL,
mm_code_reset_pass VARCHAR(100) NOT NULL,
mm_code_check_email VARCHAR(100) NOT NULL,
mm_status CHAR(1) NOT NULL DEFAULT '0',
mm_date_create_account TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
mm_date_last_login TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
mm_is_login CHAR(1) NOT NULL DEFAULT '0',
PRIMARY KEY (mm_id_user),
UNIQUE KEY (mm_email)
)ENGINE=InnoDB DEFAULT CHARSET=utf8 DEFAULT COLLATE utf8_unicode_ci;
Giving mm_email its own index will allow the foreign key in profiles to work.

MYSQL - foreign key varchar column

I would like to add localization to my database. I created languages table and I would like to add foreign key to user table:
CREATE TABLE `users` (
`id` int(11) NOT NULL,
`name` varchar(30) NOT NULL,
`email` varchar(100) NOT NULL,
`password` varchar(60) DEFAULT NULL,
`registrationDate` datetime DEFAULT current_timestamp(),
`lastLoginDate` datetime DEFAULT NULL,
`isConfirmed` tinyint(1) NOT NULL DEFAULT 0,
`activationKey` varchar(32) NOT NULL,
`resendEmail` tinyint(1) DEFAULT NULL,
`subscribedNews` tinyint(1) NOT NULL DEFAULT 1,
`activated` tinyint(1) DEFAULT 1,
`lang` varchar(5) NOT NULL DEFAULT 'en'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
ALTER TABLE `users`
ADD PRIMARY KEY (`id`),
ADD UNIQUE KEY `email` (`email`);
ALTER TABLE `users`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
create table languages(
code varchar(5) primary key,
name varchar(255) not null,
dateFormat varchar(255),
dateTimeFormat varchar(255),
currency varchar(255)
);
ALTER TABLE users
ADD lang varchar(5) NOT NULL DEFAULT 'cs';
ALTER TABLE users ADD CONSTRAINT fk_user_lang FOREIGN KEY (lang) REFERENCES languages(code);
I can't add foreign key on lang column:
error code: 150 "Foreign key constraint is incorrectly formed"
Why I can't create varchar foreign key. I would not add int primary key in languages table, because, I would like to get data from database as /api/users/cs instead of /api/users?lang=1.
Thanks
You didn't specify the character set or collation for the languages table.
Whereas users is explicitly utf8, you might be using a MySQL version where the default charset is utf8mb4 or a very old version where the default charset is latin1.
Double-check with:
SHOW CREATE TABLE languages\G
That will display the charset and collation for that table. It must be the same as the charset and collation for the foreign key column in users.
If you change the default character set of the table languages to utf8, which you defined for the table users, then it will work:
ALTER TABLE languages CONVERT TO CHARACTER SET utf8;
See the demo.

FK constraint is incorrectly formed ERROR

I got the following error message whenever I try to import this sql file to my localhost
(#1005 - Can't create table hostel_management_system.Hostel_Manager (errno: 150 "Foreign key constraint is incorrectly formed"))
Here is the sql:
DROP TABLE IF EXISTS `Hostel_Manager`;
CREATE TABLE `Hostel_Manager` (
`Hostel_man_id` int(10) NOT NULL AUTO_INCREMENT,
`Username` varchar(255) NOT NULL,
`Fname` varchar(255) NOT NULL,
`Lname` varchar(255) NOT NULL,
`Mob_no` varchar(255) NOT NULL,
`Hostel_id` int(10) NOT NULL,
`Pwd` LONGTEXT NOT NULL,
`Isadmin` tinyint(1) DEFAULT '0',
PRIMARY KEY (`Hostel_man_id`),
UNIQUE (`Username`),
KEY `Hostel_id` (`Hostel_id`),
CONSTRAINT `Hostel_Manager_ibfk_1` FOREIGN KEY (`Hostel_id`) REFERENCES `Hostel` (`Hostel_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
LOCK TABLES `Hostel_Manager` WRITE;
UNLOCK TABLES;
Hostel table :
CREATE TABLE `Hostel` (
`Hostel_id` int(10) NOT NULL AUTO_INCREMENT,
`Hostel_name` varchar(255) NOT NULL,
`current_no_of_rooms` varchar(255) DEFAULT NULL,
`No_of_rooms` varchar(255) DEFAULT NULL,
`No_of_students` varchar(255) DEFAULT NULL,
PRIMARY KEY (`Hostel_id`)
)
Why do I get that error and how to fix it?
Both the parent and children column have the same datatype and length, and the parent column is a primary colum, so that's not the issue here.
I suspect that the problem is the charset. Hostel_Manager specifies a default charset, while Hostel does not. If the default charset of your database is something else than latin1, then the foreign is malformed.
I would recommend explictly aligning the charset so it is the same for both tables (either remove both, or declare the same value).
Note that both tables also need to have the same storage engine. InnoDB is the default, so that should not be an issue, but you might want to explictly align that as well (in case the default of your database is MyISAM).

Comparing MySQL and SQLite create table statements

So I am trying to make an app with "code first" approach and active record ORM. I am willing to work with MySQL and SQLite (both) in this app. Here is how I am creating a database:
(this is a mockup table I came up with solely for this question)
$this->int("id", self::INT_MEDIUM)->unSigned()->primaryKey()->autoIncrement();
$this->string("hash", 64, self::STR_FIXED)->unique();
$this->enum("status", "available","sold","pending")->defaultValue("available");
$this->int("category", self::INT_MEDIUM)->unSigned()->defaultValue(0);
$this->string("name")->unique();
$this->text("descr")->nullable();
$this->decimal("price", 10, 4)->defaultValue(1.234);
$this->uniqueKey("store_np", "name", "price");
$this->foreignKey("category", "categories", "id");
and then my code generates CREATE TABLE statement for me, here are the results:
MySQL:
CREATE TABLE `products` (
`id` mediumint UNSIGNED PRIMARY KEY auto_increment NOT NULL,
`hash` char(64) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
`status` enum('available','sold','pending') CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL default 'available',
`category` mediumint UNSIGNED NOT NULL default 0,
`name` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
`descr` TEXT CHARACTER SET utf8 COLLATE utf8_unicode_ci default NULL,
`price` decimal(10,4) NOT NULL default 1.234,
UNIQUE KEY (`hash`),
UNIQUE KEY (`name`),
UNIQUE KEY `store_np` (`name`,`price`),
FOREIGN KEY (`category`) REFERENCES `categories`(`id`)
) ENGINE=InnoDB;
SQLite:
CREATE TABLE `products` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
`hash` TEXT UNIQUE NOT NULL,
`status` TEXT CHECK(status in ('available','sold','pending') ) NOT NULL default 'available',
`category` INTEGER UNSIGNED NOT NULL default 0,
`name` TEXT UNIQUE NOT NULL,
`descr` TEXT default NULL,
`price` REAL NOT NULL default 1.234,
CONSTRAINT `store_np` UNIQUE (`name`,`price`),
CONSTRAINT `cnstrnt_category_frgn` FOREIGN KEY (`category`) REFERENCES `categories`(`id`)
);
I executed both queries in phpMyAdmin and phpLiteAdmin, and both seemed to worked fine.
Here are my concerns:
For example, I didn't know I cannot use "UNSIGNED" with "PRIMARY KEY AUTOINCREMENT" in SQLite, which gave me hard time to figure out. Is there anything else like that I should be concerned about?
Even though both statements were executed successfully, are they going to work as expected? Especially the constraints in SQLite
Please check "status" column in SQLite, is it appropriate method to use as an alternative to MySQL's enum?

#1071 - Specified key was too long; max key length is 767 bytes

I this SQL query to create a table:
CREATE TABLE IF NOT EXISTS `local_sysDB`.`hashtags` (
`id` INT NOT NULL AUTO_INCREMENT,
`hashtag` VARCHAR(255) NOT NULL COMMENT 'hashtag must be unique. Must be saved without #',
`accountId` INT NULL,
`startTracking` DATETIME NOT NULL COMMENT 'When tracking of the hashtag start',
`endTracking` DATETIME NOT NULL COMMENT 'When tracking of the hashtag ends',
`channelInstagram` TINYINT(1) NOT NULL DEFAULT 1,
`channelTwitter` TINYINT(1) NOT NULL DEFAULT 1,
`channelYoutube` TINYINT(1) NOT NULL DEFAULT 1,
`postLimit` INT NOT NULL,
`suspendOnLimit` TINYINT(1) NOT NULL DEFAULT 1,
`created` TIMESTAMP NOT NULL DEFAULT NOW(),
`updated` TIMESTAMP NULL ON UPDATE CURRENT_TIMESTAMP,
`approveBeforeView` TINYINT(1) NOT NULL DEFAULT 0 COMMENT 'If account should approve posts before being displayed public',
`suspended` TINYINT(1) NOT NULL DEFAULT 0,
`InstagramSubscriptionId` INT(10) UNSIGNED NULL,
`deleted` TINYINT(1) NULL DEFAULT 0 COMMENT 'if hashtag is marked for deletion',
`collectedPosts` BIGINT(50) UNSIGNED NULL DEFAULT 0,
PRIMARY KEY (`id`),
UNIQUE INDEX `hashtags_hashtag` (`hashtag` ASC) KEY_BLOCK_SIZE=255,
INDEX `hashtags_accounts_accountId` (`accountId` ASC),
INDEX `hashtag_trackingDate` (`startTracking` ASC, `endTracking` ASC),
INDEX `hashtag_collectedPosts` (`collectedPosts` ASC),
INDEX `hashtag_updated` (`updated` ASC),
FULLTEXT INDEX `hashtag_search` (`hashtag` ASC),
CONSTRAINT `hashtags_accounts_accountId`
FOREIGN KEY (`accountId`)
REFERENCES `local_sysDB`.`accounts` (`id`)
ON DELETE CASCADE
ON UPDATE CASCADE)
ENGINE = InnoDB
ROW_FORMAT = COMPRESSED
KEY_BLOCK_SIZE = 16;
When I try to run this, I get the following error:
SQL-query:
CREATE TABLE IF NOT EXISTS `local_sysDB`.`hashtags` (
`id` INT NOT NULL AUTO_INCREMENT,
`hashtag` VARCHAR(255) NOT NULL COMMENT 'hashtag must be unique. Must be saved without #',
`accountId` INT NULL,
`startTracking` DATETIME NOT NULL COMMENT 'When tracking of the hashtag start',
`endTracking` DATETIME NOT NULL COMMENT 'When tracking of the hashtag ends',
`channelInstagram` TINYINT(1) NOT NULL DEFAULT 1,
`channelTwitter` TINYINT(1) NOT NULL DEFAULT 1,
`channelYoutube` TINYINT(1) NOT NULL DEFAULT 1,
`postLimit` INT NOT NULL,
`suspendOnLimit` TINYINT(1) NOT NULL DEFAULT 1,
`created` TIMESTAMP NOT NULL DEFAULT NOW(),
`updated` TIMESTAMP NULL ON UPDATE CURRENT_TIMESTAMP,
`approveBeforeView` TINYINT(1) NOT NULL DEFAULT 0 COMMENT 'If account should approve posts before being displayed public',
`suspended` TINYINT(1) NOT NULL DEFAULT 0,
`InstagramSubscriptionId` INT(10) UNSIGNED NULL,
`deleted` TINYINT(1) NULL DEFAULT 0 COMMENT 'if hashtag is [...]
MySQL meldt: Documentatie
#1071 - Specified key was too long; max key length is 767 bytes
I already found out it has something to do with this:
767 bytes is the stated prefix limitation for InnoDB tables - its
1,000 bytes long for MyISAM tables.
According to the response to this issue, you can get the key to apply
by specifying a subset of the column rather than the entire amount.
IE:
ALTER TABLE mytable ADD UNIQUE ( column1(15), column2(200) ); Tweak
as you need to get the key to apply, but I wonder if it would be worth
it to review your data model regarding this entity to see if there's
improvements that would allow you to implement the intended business
rules without hitting the MySQL limitation.
I tried adding a length to my indexes, but MySQL Workbench keeps resetting them to 0.
I'd like to know if there could be another cause of this problem, or another way to solve this problem.
I just learned a workaround... Get 5.5.14 or 5.6.3 (or later), do the SETs indicated here, and use DYNAMIC or COMPRESSED:
SET GLOBAL innodb_file_per_table = ON,
innodb_file_format = Barracuda,
innodb_large_prefix = ON;
CREATE TABLE so29676724 (
`id` INT NOT NULL AUTO_INCREMENT,
`hashtag` VARCHAR(255) NOT NULL COMMENT 'hashtag must be unique. Must be saved without #',
PRIMARY KEY (`id`),
UNIQUE INDEX `hashtags_hashtag` (`hashtag` ASC)
)
ENGINE = InnoDB
DEFAULT CHARACTER SET utf8mb4
ROW_FORMAT = COMPRESSED;
SHOW CREATE TABLE so29676724\G
mysql> CREATE TABLE so29676724 (
-> `id` INT NOT NULL AUTO_INCREMENT,
-> `hashtag` VARCHAR(255) NOT NULL COMMENT 'hashtag must be unique. Must be saved without #',
-> PRIMARY KEY (`id`),
-> UNIQUE INDEX `hashtags_hashtag` (`hashtag` ASC)
-> )
-> ENGINE = InnoDB
-> DEFAULT CHARACTER SET utf8mb4
-> ROW_FORMAT = COMPRESSED;
Query OK, 0 rows affected (0.09 sec)
"Hashes" are usually hex, not UTF-8. Hashes are usually much shorter than 255.
If either applies, then...
`hashtag`
VARCHAR(160) -- this
CHARACTER SET ascii -- and/or this
would be a solution that would work on any version without any of the innodb settings indicated.
(Note: 191 is the cutoff for VARCHAR with utf8mb4, but few standard hashes need that much.)