I'm extremely new to SQL and I'm trying to perform a simple ALTER TABLE to create a new column and use it as a foreign key to reference another simple table in my database. I've altered both tables to be InnoDB
However, when executing the ALTER TABLE code, I get the following error:
Error 1452 Cannot add or update a child row:
a foreign key constraint fails (`toys`.<result 2 when
explaining filename '#sql-6d4_6'>, CONSTRAINT
`#sql-6d4_6_ibfk_1` FOREIGN KEY (`toy_id`) REFERENCES `toys` (`toy_id`))
Below are the DESC of both tables:
Table 1:
FIELD TYPE NULL KEY EXTRA
toy_id int(11) NO PRI auto_increment
toy varchar(50) YES
Table 2:
FIELD TYPE NULL KEY EXTRA
boy_id int(11) NO PRI auto_increment
boy varchar(50) YES
And this is the ALTER query I was trying to perform:
ALTER TABLE boys
ADD COLUMN toy_id INT NOT NULL,
ADD CONSTRAINT toys_toy_id_fk
FOREIGN KEY(toy_id)
REFERENCES toys(toy_id);
I've looked all over trying to figure it out, but with no luck. Thanks in advance, and please be kind to this newbie :)
EDIT:
Here are the SHOW CREATE TABLE for both tables:
TABLE 1:
CREATE TABLE `toys` (
`toy_id` int(11) NOT NULL AUTO_INCREMENT,
`toy` varchar(50) DEFAULT NULL,
PRIMARY KEY (`toy_id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8
TABLE 2:
CREATE TABLE `boys` (
`boy_id` int(11) NOT NULL AUTO_INCREMENT,
`boy` varchar(50) DEFAULT NULL,
PRIMARY KEY (`boy_id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8
You can't add a NOT NULL column to a table that has more than zero rows, when the column is constrained to values that match those in the parent table, and yet has only NULL values because it's a new, unpopulated column with no DEFAULT.
The workaround is to do it in stages: add the column, but don't declare it NOT NULL, and don't declare the foreign key yet.
ALTER TABLE boys
ADD COLUMN toy_id INT;
Then populate it with valid data that matches some value(s) in your toys table.
UPDATE boys SET toy_id = ...;
Then alter the column to be NOT NULL, and create the constraint:
ALTER TABLE boys MODIFY COLUMN toy_id INT NOT NULL,
ADD CONSTRAINT toys_toy_id_fk
FOREIGN KEY(toy_id)
REFERENCES toys(toy_id);
Related
I have this scenario: I have those two tables:
CREATE TABLE sample_A (
ID bigint(20) UNSIGNED NOT NULL,
product varchar(60) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE sample_B (
ID bigint(20) UNSIGNED NOT NULL,
ref_id_sample_A_id bigint(20) UNSIGNED NULL,
ref_id_sample_A_ref_id bigint(20) UNSIGNED NULL,
document_name varchar(300) COLLATE utf8mb4_unicode_ci DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
and I was trying to restore a foreign key between ref_id_sample_A_id of table sample_B and ID of table sample_A but executing this instruction:
ALTER TABLE sample_B
ADD CONSTRAINT fk_sample_B_ref_sample_A_id
FOREIGN KEY (ref_id_sample_A_id)
REFERENCES sample_A(ID);
I obtain this error:
#1823 - Failed to add the foreign key constraint 'k3/fk_sample_B_ref_sample_A_id' to system tables
but I have no other foreign keys settled, neither informations if I query this:
SELECT * FROM information_schema.TABLE_CONSTRAINTS
WHERE CONSTRAINT_TYPE = 'FOREIGN KEY'
AND TABLE_NAME = 'sample_B';
I get empty result, and table simple_A is not a system table.... what I should do? Thanks in advance to all!
Cheers
I tested your sample using MySQL 8.0.26, and got this error:
ERROR 1822 (HY000): Failed to add the foreign key constraint. Missing index for constraint 'fk_sample_B_ref_sample_A_id' in the referenced table 'sample_A'
This means the referenced column, sample_A.ID, is not valid as the referenced column of a foreign key, because it is not part of a key in that table.
The referenced column must be part of a key, and ideally should be a PRIMARY KEY or UNIQUE KEY.
You did not define ID or any other column as the PRIMARY KEY for the table sample_A.
Solved right 2 sec ago anyway ... had to export dump data of the table, drop the table and recreate the table with all constraints in CREATE TABLE instruction then re-import all dump data and it worked. Thanks anyway to everyone #nbk and #Bill Karwin! :-)
I have table1 already in my db.
Table1:
CREATE TABLE `product` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`typename` varchar(255) DEFAULT NULL,
`typecode` varchar(55) DEFAULT NULL,
`parent1` int(11) DEFAULT NULL,
`parent2` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `parent1` (`parent1`),
KEY `parent2` (`parent2`)
) ENGINE=InnoDB AUTO_INCREMENT=396 DEFAULT CHARSET=latin1;
I tried to create the second table with foreign key which has reference to product.typename
this is the creation query I have used.
CREATE TABLE measurements (
id int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
age_group varchar(20) NOT NULL,
article_type varchar(255) DEFAULT NULL,
dimension text ,
createdOn int(11) NOT NULL,
updatedOn int(11) NOT NULL,
createdBy text NOT NULL,
foreign KEY(article_type) references product(typename)
)ENGINE=InnoDB AUTO_INCREMENT=396 DEFAULT CHARSET=latin1;
But this table creation is a failure with the following error.
ERROR 1215 (HY000): Cannot add foreign key constraint
I have done show engine innodb\g
------------------------
LATEST FOREIGN KEY ERROR
------------------------
2015-05-15 19:03:28 131f71000 Error in foreign key constraint of table db/measurements:
foreign KEY(article_type) references product(typename)
)ENGINE=InnoDB AUTO_INCREMENT=396 DEFAULT CHARSET=latin1:
Cannot find an index in the referenced table where the
referenced columns appear as the first columns, or column types
in the table and the referenced table do not match for constraint.
Note that the internal storage type of ENUM and SET changed in
tables created with >= InnoDB-4.1.12, and such columns in old tables
cannot be referenced by such columns in new tables.
See http://dev.mysql.com/doc/refman/5.6/en/innodb-foreign-key-constraints.html
for correct foreign key definition.
Can some one point me the problem and what is this first columns concept?
Referenced column should be Primary key. Here
foreign KEY(article_type) references product(typename)
you want to reference with typename column which is not PK.
To do it in properly way you should create table ProductType like this:
CREATE TABLE `ProductType` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`typename` varchar(255) DEFAULT NULL,
`typecode` varchar(55) DEFAULT NULL,
) ENGINE=InnoDB AUTO_INCREMENT=396 DEFAULT CHARSET=latin1;
then you can create reference like this:
CREATE TABLE measurements (
id int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
age_group varchar(20) NOT NULL,
IdProductType NOT NULL,
dimension text ,
createdOn int(11) NOT NULL,
updatedOn int(11) NOT NULL,
createdBy text NOT NULL,
foreign KEY(IdProductType) references ProductType(Id)
)ENGINE=InnoDB AUTO_INCREMENT=396 DEFAULT CHARSET=latin1;
Don't forget to do it with Product table.
Above solution is only suggestion, you have to consider your table structure yourself.
A foreign key references a key. This is usually the primary key, but doesn't have to be. In your case however you reference a column (typename) which is not defined as a key. This shows a design flaw.
You decided to use technical IDs as primary keys for your tables. You can do this. But if you do this, keep two things in mind:
You've created IDs in order to link tables easily. So don't reference a record by another column (such as typename), but by its ID.
You must still make sure that the table's natural key is unique.
As to point 2: What is your table's natural key? What is or are the fields that uniquely identify a record (apart from your technically created ID)? Is it typename? Is typename the product's name and must it be unique? Or is this typecode? Whatever it is, give this field a unique constraint, so you cannot have the same product twice in your table.
Maybe it would help you learn to design your database, if you didn't use technical IDs at all. Give it a thought.
Just a side note: Be aware that MySQL has a strange way of using the keyword KEY:
create table t (col int key);
Here KEY really means the table's primary key. col cannot be null and col must be unique. It is short for:
create table t (col int primary key);
However,
create table t (col int, key(col));
is something entirely else. Here, KEY is not short for PRIMARY KEY, but a synonym for INDEX. col can be null, col doesn't have to be unique. So better use the synonym INDEX to make it clear to a reader:
create table t (col int, index(col));
When working with an additional ID, as you are doing, you even need a unique index:
create table t (id int primary key, col int, unique index(col));
or
create table t (id int, col int, primary key(id), unique index(col));
I am having a problem setting my one-to-many relationships on PHPMyAdmin
Here is tbl_books
book_id (primary key)
bookcat_id_fk (foreign key 'on update restrict, on delete restrict')
bookname
bookdesc
Here is tbl_bookcat
bookcat_id (primary key)
bookcat_name
The relationship between the two should be that there is ONE category to MANY books, so a one-to-many relationship.
The problem that am getting would be that I am getting the error: Duplicate entry '1' for key bookcat_id_fk
This means that I am unable to assign more books to the same category because of this, so my one-to-many relationship is not working. I have searched but I could find the answer I was wondering if you could help me out.
Thanks.
I suspect your key definition bookcat_id_fk is UNIQUE, but without seeing the full dump don't know for sure. Anyway, I believe this is what you're trying to achieve:
CREATE TABLE IF NOT EXISTS `tbl_bookcat` (
`bookcat_id` int(11) NOT NULL,
`bookcat_name` varchar(255) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE IF NOT EXISTS `tbl_books` (
`book_id` int(11) NOT NULL,
`bookcat_id_fk` int(11) NOT NULL,
`bookname` varchar(255) NOT NULL,
`bookdesc` varchar(255) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
ALTER TABLE `tbl_bookcat`
ADD PRIMARY KEY (`bookcat_id`);
ALTER TABLE `tbl_books`
ADD PRIMARY KEY (`book_id`),
ADD KEY `bookcat_id_fk` (`bookcat_id_fk`);
ALTER TABLE `tbl_bookcat`
MODIFY `bookcat_id` int(11) NOT NULL AUTO_INCREMENT;
ALTER TABLE `tbl_books`
MODIFY `book_id` int(11) NOT NULL AUTO_INCREMENT;
ALTER TABLE `tbl_books`
ADD CONSTRAINT `tbl_books_ibfk_1` FOREIGN KEY (`bookcat_id_fk`) REFERENCES `tbl_bookcat` (`bookcat_id`);
I want to change the datatype of some primary-key columns in my database from INT to BIGINT. The following definition is a toy-example to illustrate the problem:
CREATE TABLE IF NOT EXISTS `owner` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`thing_id` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `thing_id` (`thing_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1 ;
DROP TABLE IF EXISTS `thing`;
CREATE TABLE IF NOT EXISTS `thing` (
`id` int(11) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1 ;
ALTER TABLE `owner`
ADD CONSTRAINT `owner_ibfk_1` FOREIGN KEY (`thing_id`) REFERENCES `thing` (`id`);
Now when i try to execut one of the following commands:
ALTER TABLE `thing` CHANGE `id` `id` BIGINT NOT NULL AUTO_INCREMENT;
ALTER TABLE `owner` CHANGE `thing_id` `thing_id` BIGINT NOT NULL;
i'm running into an error
#1025 - Error on rename of './debug/#[temp-name]' to './debug/[tablename]' (errno: 150)
SHOW INODB STATUS outputs:
LATEST FOREIGN KEY ERROR
------------------------
120126 13:34:03 Error in foreign key constraint of table debug/owner:
there is no index in the table which would contain
the columns as the first columns, or the data types in the
table do not match the ones in the referenced table
or one of the ON ... SET NULL columns is declared NOT NULL. Constraint:
,
CONSTRAINT "owner_ibfk_1" FOREIGN KEY ("thing_id") REFERENCES "thing" ("id")
I'm guessing that the foreign key definition blocks changing the column type on either side. The naive approach to solve this problem would be to delete the foreign key definitions, alter the columns and re-define the foreign keys. is there a better solution?
Even with SET foreign_key_checks = 0, you can't alter the type of the constraint column.
From MySQL doc : http://dev.mysql.com/doc/refman/5.5/en/innodb-foreign-key-constraints.html
However, even if foreign_key_checks = 0, InnoDB does not permit the creation of a foreign key constraint where a column references a nonmatching column type.
So, I agree with the comment of Devart. Just drop it and create it again.
I could suggest you to rename such fields in GUI tool - dbForge Studio for MySQL (free trial edition):
Just select the field you want to rename in the Database Explorer, click on Refactoring->Rename command, enter new name in openned window, and press OK, it will rename the field and recreate all foreign keys automatically.
Even though you changed the column size of id and thing_id, the raw data in the rows is still 4 byte integers instead of 8 byte integers. But you can convert the data.
Try this solution instead. I had to do something similar recently on a large data set, converting INT columns to BIGINT when the data set threatened to grow too large.
ALTER TABLE `owner`
DROP FOREIGN KEY `owner_ibfk_1`;
ALTER TABLE `thing` CHANGE `id` `id` BIGINT NOT NULL AUTO_INCREMENT;
ALTER TABLE `owner` CHANGE `thing_id` `thing_id` BIGINT NOT NULL;
UPDATE `thing` SET `id` = CAST(`id` AS UNSIGNED INTEGER);
UPDATE `owner` SET `thing_id` = CAST(`thing_id` AS UNSIGNED INTEGER);
-- Now the data are BIGINTs; re-create the foreign key constraint
ALTER TABLE `owner`
ADD CONSTRAINT `owner_ibfk_1` FOREIGN KEY (`thing_id`) REFERENCES `thing` (`id`);
I had a similar problem the solution is the change clause:
ALTER TABLE table_name CHANGE id id BIGINT(20) NOT NULL AUTO_INCREMENT;
That worked for me.
I have the following tables:
specie (MyIsam)
image (InnoDB)
specie_map (InnoDB)
The specie_map table should map an image to a specie, and therefore has the following columns:
specie_id
image_id
Both are int 11, just like the id columns of the specie and image tables. I know I can't create a foreign key between specie_id and specie=>id, since the specie table is a MyIsam table. However, I would expect it to be possible to create a foreign key between image_id and image=>id.
I can create that foreign key and it will save it, however, the CASCADE action I have associated with it does not work. When I delete an image, it does not delete the specie_map entry that is associated with it. I would expect this to work, as this foreign key is between InnoDB tables. Both columns are indexed and of the same data type.
Is this a limitation of MySQL, or am I doing something else wrong?
Update: as requested hereby the table definitions. I have snipped unimportant columns:
-- ----------------------------
-- Table structure for `image`
-- ----------------------------
DROP TABLE IF EXISTS `image`;
CREATE TABLE `image` (
`id` int(11) NOT NULL auto_increment,
`guid` char(36) default NULL,
`title` varchar(255) NOT NULL,
`description` text,
`user_id` int(11) NOT NULL,
`item_id` int(11) default NULL,
`date_uploaded` timestamp NOT NULL default '0000-00-00 00:00:00',
`date_created` timestamp NOT NULL default '0000-00-00 00:00:00',
`date_modified` timestamp NOT NULL default '0000-00-00 00:00:00',
`status` enum('softdeleted','tobedeleted','active') default 'active',
PRIMARY KEY (`id`),
KEY `image_user` (`user_id`),
KEY `image_item` (`item_id`),
KEY `image_mod_by` (`moderated_by`),
CONSTRAINT `image_mod_by` FOREIGN KEY (`moderated_by`) REFERENCES `user` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT `image_user` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='stores image data (not file data)';
-- ----------------------------
-- Table structure for `specie`
-- ----------------------------
DROP TABLE IF EXISTS `specie`;
CREATE TABLE `specie` (
`id` int(11) NOT NULL auto_increment,
`name` varchar(256) NOT NULL,
`commonname` varchar(256) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=22 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;
-- ----------------------------
-- Table structure for `specie_map`
-- ----------------------------
DROP TABLE IF EXISTS `specie_map`;
CREATE TABLE `specie_map` (
`id` int(11) NOT NULL auto_increment,
`image_id` int(11) NOT NULL,
`specie_id` int(11) NOT NULL,
`user_id` int(11) NOT NULL,
`karma` int(11) NOT NULL,
`date_created` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `image_id` (`image_id`),
KEY `specie_id` (`specie_id`),
CONSTRAINT `specie_map_ibfk_1` FOREIGN KEY (`image_id`) REFERENCES `image` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Foreign keys works only with InnoDb in mysql. MyISAM doesn't support them (the statements are ignored).
And is there any reason why you mix multiple DB engines?
I think you should post the exact DDL statements you used when you attempted to create these tables and the foreign key. Foreign keys between innodb tables work fine, but there are still a few things to look out for:
0) Both tables must be InnoDB. This was already highlighted by the other posters and this is probably the immediate cause of your problem.
1) the data type of the referencing columns (those that make up the foreign key) and their respective referenced columns should be the same. For example, you can't create a foreign key constrain on an INT UNSIGNED column to a plain INT column.
2) if the foreign key is created as part of the table DDL, be sure to put the foreign key definition in the constraints section, that is, below all column definitions. For example:
CREATE TABLE parent (
id int unsigned PRIMARY KEY
);
CREATE TABLE child (
parent_id int unsigned
, foreign key (parent_id)
references parent (id)
);
will work but this:
CREATE TABLE child (
parent_id int unsigned
foreign key references parent (id)
);
won't. It will fail silently because MySQL's parser ignores these types of constraint definitions even before InnoDB gets to create the table (silly, but that's how it is)
3) There must be an index over all the referenced columns. Usually the referenced columns will together make up a primary key or a unique constraint anyway, but it is your job to define this before defining the foreign key.
Final word of advice: if you think your DDL is ok but you still get an error when you execute it, for example like this:
ERROR 1005 (HY000): Can't create table 'test.child' (errno: 150)
Warning (Code 150): Create table 'test/child' with foreign key constraint failed. There is no index in the referenced table where the referenced columns appear as the first columns.
Error (Code 1005): Can't create table 'test.child' (errno: 150)
Then these errors may still not reveal the true nature of the error (silly again, but that's how it is). To shed more light on it, run this command immediately after your attempt to create the foreign key:
SHOW ENGINE INNODB STATUS;
This will give you a bunch of status info, and one section there looks like this:
------------------------
LATEST FOREIGN KEY ERROR
------------------------
120122 11:38:28 Error in foreign key constraint of table test/child:
foreign key (parent_id) references parent (id) ):
Cannot find an index in the referenced table where the
referenced columns appear as the first columns, or column types
in the table and the referenced table do not match for constraint.
Note that the internal storage type of ENUM and SET changed in
tables created with >= InnoDB-4.1.12, and such columns in old tables
cannot be referenced by such columns in new tables.
See http://dev.mysql.com/doc/refman/5.5/en/innodb-foreign-key-constraints.html
for correct foreign key definition.
As you can see, this gives a bit more information and reveals the true problem, namely "column types in the table and the referenced table do not match for constraint"
So please, post your actual DDL, I'm sure there is a problem in there somewhere.