I am creating a database table. Specifically this code will not add the two foreign keys for some reason, it says cannot add foreign key constraints, I'll show you the tables that I'm referring to.
mysql> ALTER TABLE PROGRAM
-> ADD SeriesName varchar(100) NOT NULL, ADD StartYear char(4) NOT NULL,
-> ADD CONSTRAINT program_seriesname_fk
-> FOREIGN KEY(SeriesName) REFERENCES SERIES(SeriesName),
-> ADD CONSTRAINT program_startyear_fk
-> FOREIGN KEY(StartYear) REFERENCES SERIES(StartYear);
ERROR 1215 (HY000): Cannot add foreign key constraint
SERIES table has PROGRAM table which means it is a 1:N (one-to-many relationship). So therefore, the two primary keys in SERIES become the two foreign keys in PROGRAM.
mysql> describe series;
+------------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+-------+
| SeriesName | varchar(100) | NO | PRI | | |
| StartYear | char(4) | NO | PRI | | |
| EndYear | char(4) | YES | | NULL | |
+------------+--------------+------+-----+---------+-------+
3 rows in set (0.07 sec)
mysql> describe program;
+-------------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------------+--------------+------+-----+---------+-------+
| ProgramName | varchar(50) | NO | PRI | | |
| Description | varchar(255) | YES | | NULL | |
| Recorded | date | NO | | NULL | |
+-------------+--------------+------+-----+---------+-------+
3 rows in set (0.01 sec)
You have composite PK on series table, so you can't use this fields separately in FK constraint
ADD CONSTRAINT program_seriesname_fk
FOREIGN KEY(SeriesName, StartYear) REFERENCES SERIES(SeriesName, StartYear)
To create a foreign key to a single column in InnoDB, the column you reference needs to be the first column in at least one index.
ALTER TABLE program
ADD CONSTRAINT program_startyear_fk
FOREIGN KEY(StartYear) REFERENCES SERIES(StartYear);
will not work, since SERIES(StartYear) is the second column in the primary key index on SERIES. If you add an index first though;
CREATE INDEX ix_program ON series(StartYear);
ALTER TABLE program
ADD CONSTRAINT program_startyear_fk
FOREIGN KEY(StartYear) REFERENCES SERIES(StartYear);
...it will will work.
Now if what you're really looking for is really a composite index, #Parado's answer is what you want, just wanted to point out that the index doesn't have to be composite.
Related
I want to drop just the UNIQUE Constraint from my MySQL table column and keep the Foreign Key Constraint on the column as-is. work_id is the foreign key. Initially, the column was supposed to be unique (one-to-one relationship) which is now not needed.
I'm using MySQL Ver 15.1 Distrib 5.5.64-MariaDB.
DESCRIBE requests;
+---------------------+---------------------------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------------------+---------------------------------------+------+-----+---------+-------+
| request_id | char(32) | NO | PRI | NULL | |
| owner | varchar(100) | NO | | NULL | |
| status | enum('PENDING','ACCEPTED','REJECTED') | YES | | NULL | |
| work_id | char(32) | NO | UNI | NULL | |
| response_message | varchar(3000) | YES | | NULL | |
| created_date | datetime | NO | | NULL | |
| last_modified_date | datetime | NO | | NULL | |
+---------------------+---------------------------------------+------+-----+---------+-------+
CREATE TABLE `requests` (
`request_id` char(32) NOT NULL,
`owner` varchar(100) NOT NULL,
`status` enum('PENDING','ACCEPTED','REJECTED') DEFAULT NULL,
`work_id` char(32) NOT NULL,
`response_message` varchar(3000) DEFAULT NULL,
`created_date` datetime NOT NULL,
`last_modified_date` datetime NOT NULL,
PRIMARY KEY (`request_id`),
UNIQUE KEY `work_id` (`work_id`),
CONSTRAINT `requests_ibfk_1` FOREIGN KEY (`work_id`) REFERENCES `work` (`work_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
I want to remove UNIQUE Constraint from the work_id. I did some search and executed the following commands.
SHOW INDEX FROM requests;
+-----------------+------------+----------------+--------------+-------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-----------------+------------+----------------+--------------+-------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| requests | 0 | PRIMARY | 1 | request_id | A | 16 | NULL | NULL | | BTREE | | |
| requests | 0 | work_id | 1 | work_id | A | 16 | NULL | NULL | | BTREE | | |
+-----------------+------------+----------------+--------------+-------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
And then executed
ALTER TABLE requests DROP INDEX work_id;
I get an error
ERROR 1553 (HY000): Cannot drop index 'work_id': needed in a
foreign key constraint
So, your problem is you are trying to drop a index which is used in Foreign Key Constraint. So you can not do it directly. Follow below steps:
Drop the constraint requests_ibfk_1 which is your foreign key.
alter table requests drop foreign key requests_ibfk_1
Then Drop the UNIQUE KEY on column work_id.
alter table requests drop index work_id
Again Add Foreign Key on Column work_id.
alter table requests add CONSTRAINT `requests_ibfk_1` FOREIGN KEY (`work_id`) REFERENCES `work` (`work_id`)
DEMO
The problem is that the definition of the unique constraint drops the index which is normally created for the foreign key. But there is another way, without recreating the foreign key constraint or (temporarily) disabling the checks (which can lead to consistency errors).
First, add another index for the same column (for logical reasons, I would name it exactly as the foreign key):
CREATE INDEX requests_ibfk_1 ON requests(work_id);
Now you can safely drop the unique constraint/index (since there is still an index available for the foreign key constraint):
DROP INDEX work_id ON requests;
I hope this solves the problem.
As described in #Rick James's comment , the alternative is to temporarily disable key constraints of a table, immediately drop the unique index, and then enable the keys of the table afterwards. Here is an example for mysql / mariadb :
ALTER TABLE `<YOUR_TABLE_NAME>` DISABLE KEYS;
ALTER TABLE `<YOUR_TABLE_NAME>` DROP INDEX `<YOUR_UNIQUE_INDEX_NAME>`;
ALTER TABLE `<YOUR_TABLE_NAME>` ENABLE KEYS;
I am trying to run a foreign key add query as below, with foreign key checks set to 0. Both the columns in the two tables are exactly the same. Also, both are primary keys. None of the solutions here helped in solving this problem. I'm on localhost.
mysql> alter table deliveryaddress
-> add foreign key(oid) references productorder(oid)
-> on delete cascade on update restrict;
ERROR 1822 (HY000): Failed to add the foreign key constaint. Missing index for c
onstraint '' in the referenced table 'productorder'
mysql> desc productorder;
+----------------+--------------+------+-----+-------------------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------------+--------------+------+-----+-------------------+-------+
| primenumber | varchar(15) | NO | PRI | NULL | |
| oid | varchar(10) | NO | PRI | NULL | |
| orderdatetime | timestamp | NO | | CURRENT_TIMESTAMP | |
| addressname | varchar(30) | NO | | NULL | |
| deliverycharge | decimal(8,2) | YES | | 20.00 | |
+----------------+--------------+------+-----+-------------------+-------+
5 rows in set (0.02 sec)
mysql> desc deliveryaddress;
+----------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------+-------------+------+-----+---------+-------+
| oid | varchar(10) | NO | PRI | NULL | |
| pincode | varchar(8) | NO | | NULL | |
| area | varchar(60) | NO | | NULL | |
| city | varchar(60) | NO | | NULL | |
| state | varchar(60) | NO | | NULL | |
| landmark | varchar(60) | YES | | NULL | |
| phone | varchar(15) | NO | | NULL | |
| locality | varchar(60) | NO | | NULL | |
+----------+-------------+------+-----+---------+-------+
8 rows in set (0.00 sec)
mysql>
It looks like productorder.oid is part of a multi-column primary key, and it's not the leftmost column in the primary key. (In the future, please include the result of SHOW CREATE TABLE <tablename> because it is more clear than DESCRIBE about things like multi-column keys.)
When you declare a foreign key, you must reference the leftmost column of the primary key.
When you reference a multi-column primary key, the foreign key must have the same number of columns in the same order.
Wrong (not enough columns, and referencing second column of primary key):
CREATE TABLE parent (x INT, y INT, PRIMARY KEY (x, y));
CREATE TABLE child (y INT, FOREIGN KEY (y) REFERENCES parent(y));
Wrong (individual foreign keys each referencing part of the composite primary key):
CREATE TABLE parent (x INT, y INT, PRIMARY KEY (x, y));
CREATE TABLE child (x INT, y INT,
FOREIGN KEY (x) REFERENCES parent(x),
FOREIGN KEY (y) REFERENCES parent(y)
);
Right (same columns):
CREATE TABLE parent (x INT, y INT, PRIMARY KEY (x, y));
CREATE TABLE child (x INT, y INT, FOREIGN KEY (x, y) REFERENCES parent(x, y));
Re your comment:
I'm now thinking that your real problem is that you have the relationship reversed. You are trying to declare a foreign key in deliveryaddress referencing productorder, but I would expect the reference to go the other direction.
ALTER TABLE productorder ADD FOREIGN KEY (oid) REFERENCES deliveryaddress (oid);
Then you have no error, because the primary key of deliveryaddress is just one column.
I believe this relationship makes more sense in a typical e-commerce application. There are many orders that might reference the same address. The opposite relationship is probably not what you want, because it makes no sense for many addresses to reference a single product order.
You have a composite primary key -- it consists of two keys together, (oid, primenumber).
You should use both keys to define the foreign key definition.
Alternatively, define an auto-incremented primary key and use that.
I recreated the table with index on oid that was generating missing index problem.
mysql> create table productorder
-> (
-> primenumber varchar(15) not null,
-> oid varchar(10) not null,
-> orderdatetime timestamp not null default current_timestamp,
-> addressname varchar(30) not null,
-> deliverycharge decimal(8,2) not null default 20,
-> primary key(oid, primenumber), index(oid), index(primenumber),
-> foreign key(primenumber) references user(primenumber) on delete cascade
n update restrict
-> );
Query OK, 0 rows affected (1.52 sec)
mysql> alter table deliveryaddress
-> add foreign key(oid) references productorder(oid)
-> on delete cascade on update restrict;
Query OK, 0 rows affected (0.19 sec)
Records: 0 Duplicates: 0 Warnings: 0
I am using MySQL and I have two tables of the form :
mysql> describe ing_categories;
+-------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+------------------+------+-----+---------+----------------+
| ID_Category | int(10) unsigned | NO | PRI | NULL | auto_increment |
| category | varchar(64) | NO | UNI | NULL | |
+-------------+------------------+------+-----+---------+----------------+
2 rows in set (0.00 sec)
mysql> describe ing_titles;
+----------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------+------------------+------+-----+---------+----------------+
| ID_Title | int(10) unsigned | NO | PRI | NULL | auto_increment |
| title | varchar(128) | NO | UNI | NULL | |
+----------+------------------+------+-----+---------+----------------+
2 rows in set (0.00 sec)
I have a third table containing foreign key records, the foreign keys are to the above tables:
mysql> describe ing_title_categories;
+-------------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------------+------------------+------+-----+---------+----------------+
| ID_Title_Category | int(10) unsigned | NO | PRI | NULL | auto_increment |
| ID_Title | int(10) unsigned | NO | MUL | NULL | |
| ID_Category | int(10) unsigned | NO | MUL | NULL | |
+-------------------+------------------+------+-----+---------+----------------+
How do I create the ing_titles_categories table so that the pair of foreign keys is unique?
Here is my SQL statement:
CREATE TABLE ing_title_categories
(
ID_Title_Category INTEGER UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
ID_Title INTEGER UNSIGNED NOT NULL,
ID_Category INTEGER UNSIGNED NOT NULL,
FOREIGN KEY fk_title(ID_Title)
REFERENCES ing_titles(ID_Title)
ON UPDATE CASCADE
ON DELETE RESTRICT,
FOREIGN KEY fk_category(ID_Category)
REFERENCES ing_categories(ID_Category)
ON UPDATE CASCADE
ON DELETE RESTRICT
) ENGINE=InnoDB;
I've searched other questions and they involve making an index for the foreign keys; but I don't want to index by the foreign keys, I want the constraint to be unique for the pair of foreign keys.
Tools:
MySQL Server version: 5.6.26
Notes:
I am not using PHP, but C++ via MySQL C++ Connector, so please no PHP
examples.
You add a unique or primary key constraint. Here is an example:
CREATE TABLE ing_title_categories
(
ID_Title_Category INTEGER UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
ID_Title INTEGER UNSIGNED NOT NULL,
ID_Category INTEGER UNSIGNED NOT NULL,
FOREIGN KEY fk_title(ID_Title)
REFERENCES ing_titles(ID_Title)
ON UPDATE CASCADE
ON DELETE RESTRICT,
FOREIGN KEY fk_category(ID_Category)
REFERENCES ing_categories(ID_Category)
ON UPDATE CASCADE
ON DELETE RESTRICT,
UNIQUE (ID_Title, ID_Category)
) ENGINE=InnoDB;
The implementation of the unique constraint does create an index. However, that index is needed to enforce the constraint. It will also be used for queries, where appropriate.
Note that this is equivalent to:
create unique index idx_ing_title_categories_2 on ing_title_categories(ID_Title, ID_Category);
One advantage of putting the constraint in the CREATE TABLE statement is that you can use the CONSTRAINT keyword to give the constraint a meaningful name -- useful when you want to figure out what is happening when the constraint is violated.
You can define UNIQUE KEY constraint on both columns ID_Title and ID_Category as composite unique (UNIQUE(ID_Title , ID_Category)) key which will make sure that combination of the said key columns are always unique.
ALTER TABLE ing_title_categories
ADD CONSTRAINT uniqueconstraint UNIQUE(`ID_Title` , `ID_Category`);
I really can't get a clue, as everything seems to be ok. This is the command I'm trying:
ALTER TABLE dashboard ADD CONSTRAINT FOREIGN KEY (app_name, app_parent, client)
REFERENCES apps(app_name, app_parent, client) ON DELETE CASCADE ON UPDATE CASCADE;
This is the error I get:
ERROR 1215 (HY000): Cannot add foreign key constraint
Using an explicit name for the FK doesn't help. This is the referenced table:
mysql> SHOW COLUMNS IN apps;
+------------+------------------+------+-----+------------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------+------------------+------+-----+------------+-------+
| app_name | varchar(20) | NO | PRI | | |
| app_parent | varchar(20) | NO | PRI | | |
| client | varchar(12) | NO | PRI | NULL | |
| order_idx | int(10) unsigned | YES | | 100 | |
...
And this is the referencing table:
mysql> SHOW COLUMNS IN dashboard;
+------------+----------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------+----------------------+------+-----+---------+-------+
| app_name | varchar(20) | NO | | | |
| app_parent | varchar(20) | NO | | | |
| client | varchar(12) | NO | | NULL | |
| widget | varchar(20) | YES | | NULL | |
...
Both tables have InnoDB as the engine. And this is the (rather laconic) clue I'm getting from SHOW ENGINE InnoDB STATUS:
LATEST FOREIGN KEY ERROR
------------------------
2014-06-20 11:49:57 10ec Error in foreign key constraint of table the_db/#sql-128c_2:
FOREIGN KEY (app_name, app_parent, client)
REFERENCES apps(app_name, app_parent, client) ON DELETE CASCADE ON UPDATE CASCADE:
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.
What does it mean it doesn't find an index on apps? I'm referencing primary keys! And the types are clearly the same.
EDIT: this is the CREATE command for apps:
CREATE TABLE `apps` (
`app_name` varchar(20) NOT NULL DEFAULT '',
`app_parent` varchar(20) NOT NULL DEFAULT '',
`client` varchar(12) NOT NULL,
`order_idx` int(10) unsigned DEFAULT '100',
...,
PRIMARY KEY (`app_name`,`app_parent`,`client`),
KEY `client` (`client`),
CONSTRAINT `apps_ibfk_1` FOREIGN KEY (`client`) REFERENCES `clients` (`name`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `apps_ibfk_2` FOREIGN KEY (`app_name`, `app_parent`) REFERENCES `app_list` (`name`, `parent`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=latin1
I must add that I actually managed to create a foreign key on the same columns on another table:
mysql> SHOW COLUMNS IN user_apps;
+------------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------+-------------+------+-----+---------+-------+
| app_name | varchar(20) | NO | PRI | | |
| app_parent | varchar(20) | NO | PRI | | |
| username | varchar(20) | NO | PRI | | |
| client | varchar(12) | YES | | NULL | |
...
Here, app_name, app_parent and client reference to the same named columns in apps.
This is probably the sneakiest of the mistakes: the problem was the default character set, which is latin1 for apps and utf8 for dashboard.
This solved my problem:
ALTER TABLE dashboard CONVERT TO CHARACTER SET latin1;
You better have posted output of
show create table apps
I am not sure if the referenced parent columns are indexed.
But for them to be eligible for referring from other child tables, they MUST have INDEXed
As per documentation on foreign key constraints:
REFERENCES tbl_name (index_col_name,...)
Define an INDEXes on columns app_name, app_parent, client of apps table.
If not already defined, then use following command:
ALTER TABLE apps
ADD INDEX ix_app_name( app_name )
, ADD INDEX ix_app_parent( app_parent )
, ADD INDEX ix_app_client( client )
And other primary constraints are that,
The child column definition MUST match with that of parent column.
Database ENGINE type must be same.
Refer to:
MySQL ALTER TABLE Syntax
MySQL Using FOREIGN KEY Constraints
[CONSTRAINT [symbol]] FOREIGN KEY
[index_name] (index_col_name, ...)
REFERENCES tbl_name (index_col_name,...)
[ON DELETE reference_option]
[ON UPDATE reference_option]
reference_option:
RESTRICT | CASCADE | SET NULL | NO ACTION
This question already has answers here:
Changing MySQL primary key when foreign key contraints exist
(2 answers)
Closed 8 years ago.
i created a flight class and here is the description of it.
mysql> desc flight_class;
+----------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------+--------------+------+-----+---------+-------+
| FID | varchar(5) | YES | MUL | NULL | |
| amount | decimal(7,2) | YES | | NULL | |
| no_seats | decimal(2,0) | YES | | NULL | |
| class_id | int(11) | NO | PRI | 0 | |
+----------+--------------+------+-----+---------+-------+
then i want to make the class_id to a foreign key.
to drop the primary key i said
mysql> alter table flight_class drop primary key;
ERROR 1025 (HY000): Error on rename of '.\flysafe\#sql-76c_1' to '.\flysafe\flight_class' (errno: 150)
i am a newbie to this. can any one tell me where i went wrong. or is it other tables that are affecting this table?
please do give some resource for learning these right.
thanks anirudh.
Without an index, maintaining an autoincrement column becomes too expensive, that's why MySQL requires an autoincrement column to be a leftmost part of an index.
You should remove the autoincrement property before dropping the key:
ALTER TABLE flight_class MODIFY id INT NOT NULL;
ALTER TABLE flight_class DROP PRIMARY KEY;
Note that you have a composite PRIMARY KEY which covers all three columns and id is not guaranteed to be unique.
If it happens to be unique, you can make it to be a PRIMARY KEY and AUTO_INCREMENT again:
ALTER TABLE flight_class MODIFY id INT NOT NULL PRIMARY KEY AUTO_INCREMENT;