one-to-one and one-to-many relationships - mysql

Using Mysql Workbench, I found that the sql of the one_to_one table and one_to_many table is similar. Both use a non-unique foreign key to implement their relationships.
CREATE TABLE IF NOT EXISTS `mydb`.`one_to_one` (
`id` INT NOT NULL,
`parent_id` INT NOT NULL,
PRIMARY KEY (`id`, `parent_id`),
INDEX `fk_one_to_one_parent1_idx` (`parent_id` ASC),
CONSTRAINT `fk_one_to_one_parent1`
FOREIGN KEY (`parent_id`)
REFERENCES `mydb`.`parent` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB
Consequently, I can insert rows in the one_to_one table that reference the same row in the parent table just like the one_to_many table.
Is not this a violation to the one-to-one relationship? Or should I define the one-to-one relationships with a unique foreign key?

Foreign key constraint only checks if a value of parent_id column in table one_to_one exists in parent table or not.
You can handle the issue by adding an unique index for parent_id in one_to_one table.
ALTER TABLE `mydb`.`one_to_one`
ADD UNIQUE INDEX `parent_id_UNIQUE` (`parent_id` ASC);

Related

Why KEY has automatically created when I make fk constraint for field?

I am creating the table with this syntax:
CREATE TABLE movies_genres
(
id BIGINT AUTO_INCREMENT,
movie_id INT NOT NULL,
genre_id INT NOT NULL,
PRIMARY KEY (id),
CONSTRAINT `fk_movie_id` FOREIGN KEY (movie_id) REFERENCES movies(id),
CONSTRAINT `fk_genre_id` FOREIGN KEY (genre_id) REFERENCES genres(id),
CONSTRAINT unique_id_pair UNIQUE(movie_id, genre_id)
);
But then I look at the info about the table in MySQL Workbench I see:
CREATE TABLE `movies_genres` (
`id` bigint NOT NULL AUTO_INCREMENT,
`movie_id` int NOT NULL,
`genre_id` int NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `unique_id_pair` (`movie_id`,`genre_id`),
KEY `fk_genre_id` (`genre_id`),
CONSTRAINT `fk_genre_id` FOREIGN KEY (`genre_id`) REFERENCES `genres` (`id`),
CONSTRAINT `fk_movie_id` FOREIGN KEY (`movie_id`) REFERENCES `movies` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
Why this line of code has generated?
KEY `fk_genre_id` (`genre_id`)
Also I see that extra index was created that I didn't order...
Screenshot with extra index
https://dev.mysql.com/doc/refman/8.0/en/create-table-foreign-keys.html says:
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.
(emphasis mine)
(Bill gave the answer; this is providing another tip.)
Get rid of id from the table and change to these two indexes:
PRIMARY KEY (`movie_id`,`genre_id`),
KEY `fk_genre_id` (`genre_id`),
That will make some of your uses of this many-to-many table run faster. It will also shrink the table size.
If need id
Since you need id for single-row deletion and updating, keep id, but use
PRIMARY KEY (id)
INDEX(`movie_id`, `genre_id`),
INDEX(`genre_id`, `movie_id`),
The PK will continue to make Delete/Update efficient; the other two indexes will make the many-to-many JOIN efficient.

MySQL table foreign key indexes on primary index columns

I've created a table for accounts/users with a primary key (UsersID, AccountsID) like below. Should I add the index for the Users table?
create table AccountsUsers
(
AccountsID int unsigned not null,
UsersID int unsigned not null,
Roles bigint unsigned null,
primary key (UsersID, AccountsID),
constraint AccountsUsers_Accounts_ID_fk
foreign key (AccountsID) references Accounts (ID)
on update cascade on delete cascade,
constraint AccountsUsers_Users_ID_fk
foreign key (UsersID) references Users (ID)
on update cascade on delete cascade
)
engine=InnoDB
;
create index AccountsUsers_Accounts_ID_fk
on AccountsUsers (AccountsID)
;
MySQL will create the necessary indexes for the foreign key automatically, if necessary.
In the case of your foreign key on UsersId, it can use the left column of your primary key. It doesn't need to create a new index for that foreign key.
In the case of your foreign key on AccountsId, MySQL will create a new index automatically. It can't use the fact that AccountsId is part of your primary key, because it isn't the left-most column.
After you do the CREATE TABLE, run SHOW CREATE TABLE AccountsUsers and you should see the new index it created for AccountsId.
From the documentation
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.
In other words, if you don't already have the required indexes on the columns of your referencing table (AccountsUsers), MySQL will create them for you.
If the columns in the referenced tables (Accounts and Users) are not indexed you will get an error. Your's look like they will be Primary Keys on their respective tables, so you should be fine.

Duplicate entry error in 1 column of a composite key

I am trying to insert pseudo data into my db to get going, and in one particular table I have two columns which are FK's and PK's of the table; fk_product_manf_code and fk_content_id. To my understanding, these are considered composite keys in their current state.
So I add data to the table:
fk_product_manf_code fk_content_id
NOV-ABC123 1
I then want to associate another content_id to the same product_manf_code, so I perform the following:
INSERT INTO `mydb`.`package_contents`
(`fk_product_manf_code`, `fk_content_id`)
VALUES
('NOV-ABC123', 2);
However I'm greeted with the following error:
Error Code: 1062. Duplicate entry 'NOV-ABC123' for key 'fk_product_manf_code_UNIQUE'
I don't understand what's going, because I thought a composite key makes 2 columns unique? So why is it kicking up a fuss about just 1 column being unique?
Here is the table CREATE statement
CREATE TABLE `package_contents` (
`fk_product_manf_code` varchar(255) NOT NULL,
`fk_content_id` int(11) NOT NULL,
PRIMARY KEY (`fk_content_id`,`fk_product_manf_code`),
UNIQUE KEY `fk_content_id_UNIQUE` (`fk_content_id`),
UNIQUE KEY `fk_product_manf_code_UNIQUE` (`fk_product_manf_code`),
CONSTRAINT `content_id` FOREIGN KEY (`fk_content_id`) REFERENCES `contents` (`content_id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT `product_manf_code` FOREIGN KEY (`fk_product_manf_code`) REFERENCES `products` (`product_manf_code`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
So, you are learning why composite primary keys are a pain, especially for foreign key constraints. Not only are integer keys more efficient, but a single key is easier to work with.
I would suggest changing your table structure to be more like this:
CREATE TABLE package_contents (
package_contents_id int not null auto_increment primary key,
fk_product_manf_id int NOT NULL,
fk_content_id int(11) NOT NULL,
UNIQUE KEY (fk_content_id, fk_product_manf_id),
CONSTRAINT content_id FOREIGN KEY (fk_content_id)
REFERENCES contents(content_id) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT product_manf_code FOREIGN KEY (fk_product_manf_id)
REFERENCES products(product_manf_id) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Note that I changed the manufacturer code to an id as well. This should also reduce the size of the table, assuming that the "code" is longer than 4 bytes.
If you do this for all your tables, the database will be a bit more efficient, and you won't need superfluous unique constraints. The foreign key constraints should always be to primary keys (unless there is a very good reason for using a different unique key).

MySQL Cannot drop index needed in a foreign key constraint

I need to ALTER my existing database to add a column. Consequently I also want to update the UNIQUE field to encompass that new column. I'm trying to remove the current index but keep getting the error MySQL Cannot drop index needed in a foreign key constraint
CREATE TABLE mytable_a (
ID TINYINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
Name VARCHAR(255) NOT NULL,
UNIQUE(Name)
) ENGINE=InnoDB;
CREATE TABLE mytable_b (
ID TINYINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
Name VARCHAR(255) NOT NULL,
UNIQUE(Name)
) ENGINE=InnoDB;
CREATE TABLE mytable_c (
ID TINYINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
Name VARCHAR(255) NOT NULL,
UNIQUE(Name)
) ENGINE=InnoDB;
CREATE TABLE `mytable` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`AID` tinyint(5) NOT NULL,
`BID` tinyint(5) NOT NULL,
`CID` tinyint(5) NOT NULL,
PRIMARY KEY (`ID`),
UNIQUE KEY `AID` (`AID`,`BID`,`CID`),
KEY `BID` (`BID`),
KEY `CID` (`CID`),
CONSTRAINT `mytable_ibfk_1` FOREIGN KEY (`AID`) REFERENCES `mytable_a` (`ID`) ON DELETE CASCADE,
CONSTRAINT `mytable_ibfk_2` FOREIGN KEY (`BID`) REFERENCES `mytable_b` (`ID`) ON DELETE CASCADE,
CONSTRAINT `mytable_ibfk_3` FOREIGN KEY (`CID`) REFERENCES `mytable_c` (`ID`) ON DELETE CASCADE
) ENGINE=InnoDB;
mysql> ALTER TABLE mytable DROP INDEX AID;
ERROR 1553 (HY000): Cannot drop index 'AID': needed in a foreign key constraint
You have to drop the foreign key. Foreign keys in MySQL automatically create an index on the table (There was a SO Question on the topic).
ALTER TABLE mytable DROP FOREIGN KEY mytable_ibfk_1 ;
Step 1
List foreign key ( NOTE that its different from index name )
SHOW CREATE TABLE <Table Name>
The result will show you the foreign key name.
Format:
CONSTRAINT `FOREIGN_KEY_NAME` FOREIGN KEY (`FOREIGN_KEY_COLUMN`) REFERENCES `FOREIGN_KEY_TABLE` (`id`),
Step 2
Drop (Foreign/primary/key) Key
ALTER TABLE <Table Name> DROP FOREIGN KEY <Foreign key name>
Step 3
Drop the index.
If you mean that you can do this:
CREATE TABLE mytable_d (
ID TINYINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
Name VARCHAR(255) NOT NULL,
UNIQUE(Name)
) ENGINE=InnoDB;
ALTER TABLE mytable
ADD COLUMN DID tinyint(5) NOT NULL,
ADD CONSTRAINT mytable_ibfk_4
FOREIGN KEY (DID)
REFERENCES mytable_d (ID) ON DELETE CASCADE;
> OK.
But then:
ALTER TABLE mytable
DROP KEY AID ;
gives error.
You can drop the index and create a new one in one ALTER TABLE statement:
ALTER TABLE mytable
DROP KEY AID ,
ADD UNIQUE KEY AID (AID, BID, CID, DID);
A foreign key always requires an index. Without an index enforcing the constraint would require a full table scan on the referenced table for every inserted or updated key in the referencing table. And that would have an unacceptable performance impact.
This has the following 2 consequences:
When creating a foreign key, the database checks if an index exists. If not an index will be created. By default, it will have the same name as the constraint.
When there is only one index that can be used for the foreign key, it can't be dropped. If you really wan't to drop it, you either have to drop the foreign key constraint or to create another index for it first.
Because you have to have an index on a foreign key field you can just create a simple index on the field 'AID'
CREATE INDEX aid_index ON mytable (AID);
and only then drop the unique index 'AID'
ALTER TABLE mytable DROP INDEX AID;
I think this is easy way to drop the index.
set FOREIGN_KEY_CHECKS=0; //disable checks
ALTER TABLE mytable DROP INDEX AID;
set FOREIGN_KEY_CHECKS=1; //enable checks
drop the index and the foreign_key in the same query like below
ALTER TABLE `your_table_name` DROP FOREIGN KEY `your_index`;
ALTER TABLE `your_table_name` DROP COLUMN `your_foreign_key_id`;
Dropping FK is tedious and risky. Simply create the new index with new columns and new index name, such as AID2. After the new Unique Index is created, you can drop the old one with no issue. Or you can use the solution given above to incorporate the "drop index, add unique index" in the same alter table command. Both solutions will work
In my case I dropped the foreign key and I still could not drop the index. That was because there was yet another table that had a foreign key to this table on the same fields. After I dropped the foreign key on the other table I could drop the indexes on this table.
If you are using PhpMyAdmin sometimes it don't show the foreign key to delete.
The error code gives us the name of the foreign key and the table where it was defined, so the code is:
ALTER TABLE your_table DROP FOREIGN KEY foreign_key_name;
You can show Relation view in phpMyAdmin and first delete foreign key. After this you can remove index.
You can easily check it with DBeaver. Example:
As you can see there are 3 FKs but only 2 FK indexes. There is no index for FK_benefCompanyNumber_beneficiaries_benefId as UK index provide uniqueness for that FK.
To drop that UK you need to:
DROP FK_benefCompanyNumber_beneficiaries_benefId
DROP UK
CREATE FK_benefCompanyNumber_beneficiaries_benefId
The current most upvoted answer is not complete.
One needs to remove all the foreign keys whose "source" column is also present in the UNIQUE KEY declaration.
So in this case, it is not enough to remove mytable_ibfk_1 for the error to go away, mytable_ibfk_2 and mytable_ibfk_3 must be deleted as well.
This is the complete answer:
ALTER TABLE mytable DROP FOREIGN KEY mytable_ibfk_1;
ALTER TABLE mytable DROP FOREIGN KEY mytable_ibfk_2;
ALTER TABLE mytable DROP FOREIGN KEY mytable_ibfk_3;
Its late now but I found a solution which might help somebody in future.
Just go to table's structure and drop foreign key from foreign keys list. Now you will be able to delete that column.

delete primary key also deletes foreign key

I have two table, with a PK of ID. If say I delete the entry for ID = 1, I want it to automatically delete the FK of ID in the other table. In other words I want so that it should also delete all entries with ID = 1 in the other table. How can I do this? I have linked the PK-FK relationship, but when I delete the entry with ID 1 in the PK table it doesn't delete the FK.
Make sure you're using the InnoDB engine for both tables, and add a foreign-key constraint specifying on delete cascade. Your table creation SQL should look something like this:
create table child_table (
parent_id int references parent_table(id) on delete cascade
) engine 'innodb';
where child_table and parent_table are the names of your child and parent tables.
You have to define your Foreign Key constraints as ON DELETE CASCADE.
Note: You need to use InnoDB storage engine, he default MyISAM storage engine not support foreign keys relation.
CREATE TABLE `table2` (
`id` int(11) NOT NULL auto_increment,
`name` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `ids` (`ids`)
CONSTRAINT `foreign` FOREIGN KEY (`ids`)
REFERENCES `table2` (`ids`) ON DELETE CASCADE ON UPDATE CASCADE
)