Making foreign key to unique index column in mysql - mysql

Products
CREATE TABLE if NOT EXISTS `PRODUCTS` (
`ID` INT unsigned NOT NULL AUTO_INCREMENT,
`COMPANY_ID` INT(10) unsigned NOT NULL,
`PRODUCT_CODE` VARCHAR(5) NOT NULL,
`PRODUCT_NAME` VARCHAR(15) NOT NULL,
`UNIT_TYPE` VARCHAR(1) NULL,
UNIQUE INDEX UNIQUE_COMAPNY_PRODUCT_CODE (`COMPANY_ID`, `PRODUCT_CODE`),
CONSTRAINT `PRODUCT_COMPANY_ID_FK` FOREIGN KEY (`COMPANY_ID`) REFERENCES `companies` (`ID`) ON DELETE CASCADE ON UPDATE CASCADE
)
and i want to make a foreign key to the upper 'PRODUCT_CODE' ......
here is my code
ALTER TABLE services
ADD COLUMN `PRODUCT_CODE` VARCHAR(5),
// ADD foreign `PRODUCT_CODE` that refrences to `PRODUCT_CODE` in PRODUCTS table
So how to do the commented line above in mysql ??

The column referenced in a foreign key may be either a Primary Key or Unique. You need to first add an index on PRODUCT_CODE. I have taken it as UNIQUE
ALTER TABLE PRODUCTS ADD UNIQUE INDEX (PRODUCT_CODE);
Then, you can use ALTER TABLE .. ADD FOREIGN KEY syntax:
ALTER TABLE services
ADD FOREIGN KEY (PRODUCT_CODE) REFERENCES PRODUCTS(PRODUCT_CODE);
Combining this to your existing ALTER TABLE query, a single query would look as follows:
ALTER TABLE services
ADD COLUMN `PRODUCT_CODE` VARCHAR(5),
ADD FOREIGN KEY (PRODUCT_CODE) REFERENCES PRODUCTS(PRODUCT_CODE);
If you already have some data in the services table, which has certain PRODUCT_CODE value, which do not exist in the PRODUCTS table. You will get the following error:
Error Code: 1215. Cannot add foreign key constraint
In that case, you will need to fix the data in the tables. You may check this answer for the tips: https://stackoverflow.com/a/53099922/2469308

Related

Why is MySQL not properly naming my foreign key?

Please consider the following SQL code that I run in MySQL 8.0.22 (in an InnoDB database):
CREATE TABLE `person` (
`person_id` smallint unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(128) NOT NULL,
PRIMARY KEY (`person_id`)
);
CREATE TABLE `pet` (
`pet_id` smallint unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(128) NOT NULL,
PRIMARY KEY (`pet_id`)
);
ALTER TABLE `pet`
ADD COLUMN `owner_id` smallint unsigned;
ALTER TABLE `pet`
ADD CONSTRAINT `fk_pet_person`
FOREIGN KEY `idx_fk_pet_person` (`owner_id`)
REFERENCES `person` (`person_id`);
SHOW CREATE TABLE pet;
The output of SHOW CREATE TABLE pet is:
CREATE TABLE `pet` (
`pet_id` smallint unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(128) NOT NULL,
`owner_id` smallint unsigned DEFAULT NULL,
PRIMARY KEY (`pet_id`),
KEY `fk_pet_person` (`owner_id`),
CONSTRAINT `fk_pet_person` FOREIGN KEY (`owner_id`) REFERENCES `person` (`person_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
In the output above, why is the KEY named fk_pet_person when I specified its name as idx_fk_pet_person in my ALTER TABLE command? How can I get it to be named so?
You are confusing the FOREIGN KEY and the INDEX that makes it work. Pay attention - there is NO expression name (which looks like an index definition but is not) in the constraint definition displayed in your code.
When there is no suitable index for the constraint to work, then this index is auto-created, and the constraint name is used as the index name. If a suitable index exists, then auto-creation does not occur.
If you want the index to have a defined name then you must create this index in a separate ALTER TABLE (sub)statement before the constraint creation:
ALTER TABLE `pet`
ADD KEY `idx_fk_pet_person` (`owner_id`),
ADD CONSTRAINT `fk_pet_person`
FOREIGN KEY (`owner_id`)
REFERENCES `person` (`person_id`);
or
ALTER TABLE `pet`
ADD KEY `idx_fk_pet_person` (`owner_id`);
ALTER TABLE `pet`
ADD CONSTRAINT `fk_pet_person`
FOREIGN KEY (`owner_id`)
REFERENCES `person` (`person_id`);
DEMO
MySQL documentation specifies this syntax for creating a foreign key:
[CONSTRAINT [symbol]] FOREIGN KEY
[index_name] (col_name, ...)
REFERENCES tbl_name (col_name,...)
[ON DELETE reference_option]
[ON UPDATE reference_option]
reference_option:
RESTRICT | CASCADE | SET NULL | NO ACTION | SET DEFAULT
There are two reasons why my code does not set the [index_name] and instead the index name is automatically made the same as the constraint name. Both are evident from this paragraph taken from the MySQL 8.0 Reference Manual, section 13.1.20.5 FOREIGN KEY Constraints:
Prior to MySQL 8.0.16, if the CONSTRAINT symbol clause was not defined, or a symbol was not included following the CONSTRAINT keyword, both InnoDB and NDB storage engines would use the FOREIGN_KEY index_name if defined. In MySQL 8.0.16 and higher, the FOREIGN_KEY index_name is ignored.
The first reason is that I am running the code in MySQL 8.0.22.
The second reason is that I should have omitted the constraint name.
The workarounds in #akina's answer indeed enable me to specify a name for the foreign key (index) that is different from the constraint's name.

Odd Cannot add foreign key constraint

I have an odd one. I cannot create a table using the following:
The table Users already exists in the DB, only UserTimeZones is to be added, and it fails.
CREATE TABLE `Users` (
`AccessFailedCount` int(11) NOT NULL,
`EmailConfirmed` bit(1) NOT NULL,
`Id` char(36) NOT NULL,
`NormalizedUserName` varchar(256) DEFAULT NULL,
`NormalizedEmail` varchar(256) DEFAULT NULL,
PRIMARY KEY (`Id`),
UNIQUE KEY `UserNameIndex` (`NormalizedUserName`),
KEY `EmailIndex` (`NormalizedEmail`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE `UserTimeZones` (
`Id` char(36) NOT NULL,
`UserId` char(36) NOT NULL,
`TimeZoneOffsetInSeconds` int NOT NULL,
`LastUpdatedAt` datetime(6) NOT NULL,
CONSTRAINT `PK_UserTimeZones` PRIMARY KEY (`Id`),
CONSTRAINT `FK_UserTimeZones_Users_UserId` FOREIGN KEY (`UserId`) REFERENCES `Users` (`Id`) ON DELETE CASCADE
);
SHOW ENGINE INNODB STATUS;
Here is what the status shows:
------------------------ LATEST FOREIGN KEY ERROR
2018-11-09 11:26:44 0x7f832c523700 Error in foreign key constraint of
table fifty/UserTimeZones:
FOREIGN KEY (UserId) REFERENCES Users (Id) ON DELETE 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.
Please refer to
http://dev.mysql.com/doc/refman/5.7/en/innodb-foreign-key-constraints.html
for correct foreign key definition.
So I have the classic "Cannot add foreign key constraint".
What I have tried:
Placing the Users.Id column as first column : doesn't change anything
The column types are the same, the engines too...
Applying the migration without data in the DB -> it works
Running the script in a DB without data -> still doesn't work...
What is the problem?
Not sure it matters but I use entity framework core.
https://dev.mysql.com/doc/refman/8.0/en/innodb-foreign-key-constraints.html
Foreign key definitions for InnoDB tables are subject to the following
conditions:
InnoDB permits a foreign key to reference any index column or group of
columns. However, in the referenced table, there must be an index
where the referenced columns are listed as the first columns in the
same order.
But oddly the index seems to be needed on the referencing table:
https://dev.mysql.com/doc/refman/8.0/en/create-table-foreign-keys.html#foreign-keys-examples
So try adding an index on your table
CREATE TABLE `UserTimeZones` (
`Id` char(36) NOT NULL,
`UserId` char(36) NOT NULL,
`TimeZoneOffsetInSeconds` int NOT NULL,
`LastUpdatedAt` datetime(6) NOT NULL,
CONSTRAINT `PK_UserTimeZones` PRIMARY KEY (`Id`),
INDEX userid_ind (UserId),
CONSTRAINT `FK_UserTimeZones_Users_UserId` FOREIGN KEY (`UserId`) REFERENCES `Users` (`Id`) ON DELETE CASCADE
);
Note: this is what the error message says.

Unable to create foreign key in database

I am trying to create a table with a varchar column as foreign key but MySQL gives me an error while creating the table. My query is like this:
CREATE TABLE `survey_hesco_subdivision` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`circle_code` VARCHAR(100) DEFAULT NULL,
`name` VARCHAR(100) DEFAULT NULL,
`circle_name` VARCHAR(100) DEFAULT NULL,
`division_code` VARCHAR(100) DEFAULT NULL,
`sub_div_code` VARCHAR(100) NOT NULL,
`division_name` VARCHAR(100) DEFAULT NULL,
PRIMARY KEY (`id`,`sub_div_code`),
KEY `id` (`id`)
) ENGINE=INNODB AUTO_INCREMENT=91 DEFAULT CHARSET=latin1;
The above table is already in used
Create table `accurate_mam`.`meter_ping`(
`id` int(11) NOT NULL AUTO_INCREMENT,
`meter_id` int(11) NOT NULL,
`meter_msn` varchar(100),
`sub_div_code` varchar(100) NOT NULL,
`sub_div_name` varchar(100),
primary key (`id`),
constraint `FK_PING_METER_ID` foreign key (`meter_id`) references
`accurate_mam`.`meters`(`id`) on delete Cascade,
constraint `FK_PIN_SUB_DIV` foreign key (`sub_div_code`) references
`accurate_mam`.`survey_hesco_subdivision`(`sub_div_code`) on delete Cascade
) ENGINE=InnoDB charset=latin1 collate=latin1_swedish_ci
The error I am getting is
Error Number : 1005
Error Message: Can't create table accurate_mam.meter_ping (errno: 150 "Foreign key constraint is incorrectly formed")
I have already looked into this question
MySQL requires indexes on foreign keys and referenced keys so that foreign key checks can be fast and not require a table scan. In the referencing table, there must be an index where the foreign key columns are listed as the first columns in the same order.
InnoDB permits a foreign key to reference any index column or group of
columns. However, in the referenced table, there must be an index
where the referenced columns are listed as the first columns in the
same order.
So, just create a index like this, before creating child table :
CREATE INDEX `idx_survey_hesco_subdivision_sub_div_code` ON survey_hesco_subdivision(sub_div_code);
Although, It is not best practice to use non-unique column as reference columns in relationship. DELETE CASCADE will not behave properly in that case. I will suggest you create a unique key on sub_div_code of primary table as well.
For more details, refere to this
Source : Cannot add foreign key - StackOverflow
Have you already run CREATE TABLE meters? There error is caused by that table being missing. Let's see that CREATE.

Foreign key to a group of attributes given as primary key

I have a Class table with the primary keys as Section, Semester and Department. Another Student table with a USN as the primary key. First why does it allow me to reference Class.Semester from Student.SEM? Shouldn't I only be allowed to reference the primary key in Class from a group of attributes as foreign key in the Student? I also tried to reference Class.section from Student.Class and I get an error saying #1215 - Cannot add foreign key constraint.
CREATE TABLE `Class` (
`Semester` int(1) NOT NULL,
`Section` varchar(1) NOT NULL,
`Department` varchar(3) NOT NULL,
`CTID` varchar(10) NOT NULL DEFAULT ''
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
ALTER TABLE `Class`
ADD PRIMARY KEY (`Semester`,`Section`,`Department`);
CREATE TABLE `student` (
`USN` varchar(10) NOT NULL,
`DOB` date DEFAULT NULL,
`Class` varchar(1) NOT NULL,
`SEM` int(1) NOT NULL,
`Dep` varchar(3) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT;
ALTER TABLE `student`
ADD PRIMARY KEY (`USN`),
ADD KEY `Class` (`Class`,`SEM`,`Dep`),
ADD KEY `SEM` (`SEM`);
ALTER TABLE `student`
ADD CONSTRAINT `student_ibfk_1` FOREIGN KEY (`SEM`) REFERENCES `Class` (`Semester`) ON DELETE CASCADE ON UPDATE CASCADE;
MySQL supports a smaller number of columns from the available keys as long as the list of columns starts with the list of columns in the key, as described in 13.1.17.6 Using FOREIGN KEY Constraints:
MySQL requires indexes on foreign keys and referenced keys so that foreign key checks can be fast and not require a table scan. In the referencing table, there must be an index where the foreign key columns are listed as the first columns in the same order. Such an index is created on the referencing table automatically if it does not exist. This index might be silently dropped later, if you create another index that can be used to enforce the foreign key constraint. index_name, if given, is used as described previously.
InnoDB permits a foreign key to reference any column or group of columns. However, in the referenced table, there must be an index where the referenced columns are listed as the first columns in the same order.
So in your case you can use the column Semester because it's the start of your index (Semester, Section, Department), but you cannot use Section or Department.

Mysql can not add foreign key

I have a study case where three tables has to be created. there creation statements are as following.
create table COMMUNITY(
c_id varchar(10) primary key,
name varchar(30) not null,
longitude float,
latitude float,
post_code varchar(15) not null,
key(c_id))
create table UNIT(
c_id varchar(10) not null,
u_id int not null,
name varchar(20) not null,
key(c_id, u_id),
primary key(c_id, u_id),
constraint unique(c_id, u_id),
constraint FK_UNIT foreign key(c_id) references COMMUNITY(c_id)
on delete cascade on update cascade)
create table ROOM(
r_id int not null,
u_id int not null,
c_id varchar(10) not null,
name varchar(20),
primary key(c_id, u_id, r_id),
constraint FK_ROOM_UID foreign key(u_id) references UNIT(u_id)
on delete cascade on update cascade,
constraint FK_ROOM_CID foreign key(c_id) references UNIT(c_id)
on delete cascade on update cascade)
the Community and unit tables are created successfully, but when I try to create room, mysql gives me Error Code: 1215. Cannot add foreign key constraint
I wonder what's going here and How I can create them? (I knew InnoDB can solve this problem, but is there any other way I can do that?)
Thanks
This looks like the culprit of the actual error you're seeing:
constraint FK_ROOM_UID foreign key(u_id) references UNIT(u_id)
constraint FK_ROOM_UID foreign key(c_id) references UNIT(c_id)
It should be:
constraint FK_ROOM_UID foreign key(c_id, u_id) references UNIT(c_id, u_id)
Referencing the double-column key in the UNIT table
You'll also need to use InnoDB to actually create the foreign key indexes, so:
CREATE TABLE UNIT(
....
) ENGINE=InnoDB;
I finally fugured this out, according to the MySQL log
"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."
All I have to do is to create primary key at the first column of a table and make sure the type matches. so I changed my statements as following
create table UNIT(
primary key(c_id, u_id),
c_id varchar(10) not null,
u_id int not null,
name varchar(20) not null,
constraint unique(c_id, u_id),
constraint FK_UNIT foreign key(c_id) references COMMUNITY(c_id)
on delete cascade on update cascade)
create table ROOM(
primary key(c_id, u_id, r_id),
r_id int not null,
u_id int not null,
c_id varchar(10) not null,
name varchar(20),
constraint FK_ROOM_UID foreign key(c_id, u_id)
references UNIT(c_id, u_id) on delete cascade on update cascade)
Now I can create tables properly. I have been struggling on this problem since yesterday.