Foreign Key CASCADE DELETE for table with multiple parents? - mysql

I am very new to foreign keys and CASCADE operations, and am wondering about the following scenario:
Let's say I have two completely different parent tables (HUMAN and ALIEN) with completely different data. Now, what I want to do is create a child table named SPACESHIP, as both humans and aliens can own spaceships, with an FK that would point to either a row in HUMAN or a row in ALIEN, in the hopes that if I delete either a human row or an alien row, it would Cascade Delete the corresponding spaceship rows owned by the respective parent. Is there a good way to do this with FK's and CASCADE DELETE? Or should I just have a column in HUMAN and ALIEN called spaceship_id and handle the delete of spaceships manually / via trigger? Some other course of action?
Help!

This can be solved by adding a supertype table (lets call it Being) that has as subtypes the two Human and Alien tables. Then the FOREIGN KEY constraint would reference this superype table.
The supertype:
CREATE TABLE Being
( BeingId INT AUTO_INCREMENT
, ... other stuff about beings
, PRIMARY KEY (BeingId)
) ENGINE = InnoDB ;
the subtypes:
CREATE TABLE Human
( BeingId INT --- not AUTO_INCREMENT
, ... other stuff about humans
, PRIMARY KEY (BeingId)
, CONSTRAINT Being_Human_fk
FOREIGN KEY (BeingId)
REFERENCES Being(BeingId)
ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE = InnoDB ;
CREATE TABLE Alien
( BeingId INT --- not AUTO_INCREMENT
, ... other stuff about aliens
, PRIMARY KEY (BeingId)
, CONSTRAINT Being_Alien_fk
FOREIGN KEY (BeingId)
REFERENCES Being(BeingId)
ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE = InnoDB ;
and the Spaceship table:
CREATE TABLE Spaceship
( SpaceshipId
, SpaceshipName
, OwnerId
, ... other stuff about spaceships
, PRIMARY KEY (SpaceshipId)
, CONSTRAINT Being_Spaceship_fk
FOREIGN KEY (OwnerId)
REFERENCES Being(BeingId)
ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE = InnoDB ;

If you use InnoDB storage engine, then you can use foreign keys, because currently only InnoDB supports them. Otherwise you can use triggers or delete parent and child records using one query, e.g.:
DELETE HUMAN, SPACESHIP
FROM HUMAN
JOIN SPACESHIP
ON HUMAN.id = SPACESHIP.id
WHERE HUMAN.id = 1;

Related

Delete records with recursive foreign key constraints

What is the best approach to delete records from a table which has a foreign key to another table and the other table has a foreign key to it?
For instance, I have the following tables. accounts has one or more networks and accounts must have a single default network.
CREATE TABLE IF NOT EXISTS accounts (
id INT NOT NULL,
networksId INT NOT NULL,
PRIMARY KEY (id),
INDEX fk_accounts_networks1_idx (networksId ASC),
CONSTRAINT fk_accounts_networks1
FOREIGN KEY (networksId)
REFERENCES networks (id)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
CREATE TABLE IF NOT EXISTS networks (
id INT NOT NULL AUTO_INCREMENT,
accountsId INT NOT NULL,
PRIMARY KEY (id),
INDEX fk_sites_accounts1_idx (accountsId ASC),
CONSTRAINT fk_sites_accounts1
FOREIGN KEY (accountsId)
REFERENCES accounts (id)
ON DELETE CASCADE
ON UPDATE NO ACTION)
ENGINE = InnoDB;
Even though I have a CASCADE DELETE on networks FK to accounts, I tried to explicitly delete networks in hopes of getting around the foreign key constraint, however, was not successful.
DELETE a, n FROM accounts a INNER JOIN networks n ON n.accountsId=a.id WHERE a.id=123;
Is my only solution something like the following?
SET FOREIGN_KEY_CHECKS=0;
DELETE FROM networks WHERE accountsId=123;
SET FOREIGN_KEY_CHECKS=1;
DELETE FROM accounts WHERE id=123;
You have a constraint interdependency between both tables. This is not a good design, because it tends to make DDL and DML operations more complicated than normally necessary.
Apart from temporarily disabling a foreign key, the only possible solution to perform a successful DELETE is to make one of the referencing fields nullable so you can break the relation by setting it to NULL before deleting. The ON DELETE CASCADE will take care of dropping the related record in the other table.
Make one the referencing fields nullable :
ALTER TABLE networks MODIFY accountsId INT NULL;
Delete :
UPDATE network SET accountsId = NULL WHERE id = ?;
DELETE FROM network WHERE id = ?;
-- the corresponding record in table accounts gets automatically deleted.

tables related with foreign key are synched with each other

I am using phpmyadmin for mysql. I have 4 tables project1, project2, project3 and combine table. suppose combine table is connected to all other tables with the foreign keys and we add some data with the help of some background script to project1, prject2, and project3 tables. Is there any way to update the corresponding foreign keys in the combine table automatically ( without manually updating the record). I am using a yii framework for the GUI.
Please suggest some way as I am new to mysql and yii framework.
http://dev.mysql.com/doc/refman/5.6/en/create-table-foreign-keys.html
Not fully understanding your question but I think you are referring to ON DELETE and ON UPDATE.
ON DELETE & ON UPDATE options
CASCADE
SET NULL
NO ACTION
RESTRICT
ON DELETE & ON CASCADE are placed as constraints in the FK table and they occur when parent ID is either deleted or updated.
So if you change an id within the projects table and you wish for this change to be reflected in the combine table, you would use ON UPDATE CASCADE.
As a side note, why do you have 4 tables? I can only see the need for 2 tables.
Please note that SQL below may not be syntactically correct.
CREATE TABLE tbl_projects (
id integer NOT NULL PRIMARY KEY AUTO INCREMENT,
name varchar(255),
...
...
);
Method 1 creating a row for each project in the combine table:
CREATE TABLE tbl_combine (
id integer NOT NULL PRIMARY KEY AUTO INCREMENT,
project_id integer,
...
CONSTRAINT `FK_combine_project`
FOREIGN KEY (`project_id`)
REFERENCES `tbl_project` (`id`)
ON DELETE CASCADE ON UPDATE CASCADE
);
Method 2:
CREATE TABLE tbl_combine (
id integer NOT NULL PRIMARY KEY AUTO INCREMENT,
project1_id integer,
project2_id integer,
project3_id integer,
...
CONSTRAINT `FK_combine_project1`
FOREIGN KEY (`project1_id`)
REFERENCES `tbl_project` (`id`)
ON DELETE CASCADE ON UPDATE CASCADE
CONSTRAINT `FK_combine_project2`
FOREIGN KEY (`project2_id`)
REFERENCES `tbl_project` (`id`)
ON DELETE CASCADE ON UPDATE CASCADE
CONSTRAINT `FK_combine_project3`
FOREIGN KEY (`project3_id`)
REFERENCES `tbl_project` (`id`)
ON DELETE CASCADE ON UPDATE CASCADE
);
You can also do this via GUI in phpmyadmin by setting the foreign keys as an index by clicking a button, then going to the table relation view and choosing your options.
Hope this helps - I have attached an phpmyadmin image for you to see.

MySQL error 1022 when creating table

MySQL Workbench came up with the following SQL to create a table:
CREATE TABLE IF NOT EXISTS `mydb`.`errors_reports` (
`error_id` INT NOT NULL ,
`report_short` VARCHAR(15) NOT NULL ,
PRIMARY KEY (`error_id`, `report_short`) ,
INDEX `error_id_idx` (`error_id` ASC) ,
INDEX `report_short_idx` (`report_short` ASC) ,
CONSTRAINT `error_id`
FOREIGN KEY (`error_id` )
REFERENCES `mydb`.`errors` (`error_id` )
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `report_short`
FOREIGN KEY (`report_short` )
REFERENCES `mydb`.`reports` (`report_short` )
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB
which looks fine to me, and there are a bunch of other very similar tables in my database which MySQL was perfectly happy to create.
But this one...
ERROR 1022 (23000): Can't write; duplicate key in table 'errors_reports'
I can't for the life of me see any duplicate keys here. There's only one key defined!
I'm running MySQL 5.6 with a fresh default install. There's nothing in the error log.
Ideas?
Edit: through a process of elimination (going back to the simplest possible definition of the table, then gradually adding bits back in) the problem appears to be this bit:
CONSTRAINT `error_id`
FOREIGN KEY (`error_id` )
REFERENCES `mydb`.`errors` (`error_id` )
ON DELETE NO ACTION
ON UPDATE NO ACTION,
which is particularly odd as there is identical code in several other table definitions and those are perfectly okay!
The problem is that the name of a foreign key can not be the same as another foreign key in the entire model.
Imagine this situation
Catalog --> Supplier
Product --> Supplier
if the name of the foreign key in table Catalog for supplier is "supplier" and you assigned the same name in product table then the foreign keys names will "collide".
You need to name them differently..
For example:
catalog_supplier
product_supplier
It seems you're creating an index on the foreign key columns. When creating a foreign key in InnoDb, one will be created automatically.
See this thread.
Try using INSERT IGNORE instead of INSERT where INSERT IGNORE will not insert a new row if a duplicate primary key is found. This should help resolve the problem temporary but I would recommend truncating the table.

Creating foreign keys on already indexed columns with MySQL Workbench

I am creating a database model with Workbench and create the following table:
CREATE TABLE IF NOT EXISTS `Database`.`table1` (
`idtable1` INT NOT NULL ,
`uniquecolumn` INT NOT NULL ,
PRIMARY KEY (`idtable1`) ,
UNIQUE INDEX `UniqueIndex` (`uniquecolumn` ASC) )
ENGINE = InnoDB
It has a primary key, and a unique key on my second column.
When I create foreign key constraints on them, Workbench automatically adds two indexes:
CREATE TABLE IF NOT EXISTS `Database`.`table1` (
`idtable1` INT NOT NULL ,
`uniquecolumn` INT NOT NULL ,
PRIMARY KEY (`idtable1`) ,
UNIQUE INDEX `UniqueIndex` (`uniquecolumn` ASC) ,
INDEX `FKOne` (`idtable1` ASC) , //here
INDEX `FKTwo` (`uniquecolumn` ASC) , //(I don't want this!)
CONSTRAINT `FKOne`
FOREIGN KEY (`idtable1` )
REFERENCES `Database`.`table2` (`idtable2` )
ON DELETE CASCADE
ON UPDATE CASCADE,
CONSTRAINT `FKTwo`
FOREIGN KEY (`uniquecolumn` )
REFERENCES `Database`.`table2` (`idtable2` )
ON DELETE CASCADE
ON UPDATE CASCADE)
ENGINE = InnoDB
(The above is the forward-engineered script after adding the foreign keys to my model)
I have four indexes now.
This is what the MySQL Reference Manual says:
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.
So I understand there is no need to create indexes FKOne and FKTwo, since there are already a Primary Key and a Unique index, on the same columns, in the same order. Yet MySQL Workbench doesn't allow me to delete indexes FKOne and FKTwo. And I think I should be able to do this:
CREATE TABLE IF NOT EXISTS `Database`.`table1` (
`idtable1` INT NOT NULL ,
`uniquecolumn` INT NOT NULL ,
PRIMARY KEY (`idtable1`) ,
UNIQUE INDEX `UniqueIndex` (`uniquecolumn` ASC) ,
CONSTRAINT `FKOne`
FOREIGN KEY (`idtable1` )
REFERENCES `Database`.`table2` (`idtable2` )
ON DELETE CASCADE
ON UPDATE CASCADE,
CONSTRAINT `FKTwo`
FOREIGN KEY (`uniquecolumn` )
REFERENCES `Database`.`table2` (`idtable2` )
ON DELETE CASCADE
ON UPDATE CASCADE)
ENGINE = InnoDB
Am I right? Would this code work? Is there some way to do it with Workbench? (Apart from deleting those two lines at the last moment before forward-engineering).
Or maybe MySQL is smart enough to refrain from creating totally redundant indexes and I don't have to worry about it...?
(I'm assuming this is when defining a model.)
See Bug 53277, where I mention the following obscure workaround:
You start with a foreign key and its corresponding generated index that you want to get rid of. Make sure the key is (at least temporarily) on a single non-unique column. In the Indexes tab, change the Type to UNIQUE. Then go to the Columns tab, where UQ is now checked, and uncheck it. The unwanted index is eliminated!

Why does table-create fail when everything's innodb, but not when one table is MyIsam?

I find innodb quite annoying when I try to design a db structure, at least compared with MyIsam, which seems to have less limitations
Say, if I want to create a simple library system.
And I have four tables.
1,table book_item, which records the book_name, author, publish time and those basic information about books
2, table book, which represents a specific real object of the book item. So a book_item object can relate to many book objects.
3, table tag, which represents a book tag. Like science, literature, architecture and so on.
4, table tag_book_item_relation, which relates tags to book_items.
So, the relations are as below.
1,we have a book item to book is one-to-many relationship
2,book_item to tag is many-to-many relationship.
Note here, engine for the table are all innodb
If I try to create the tables, it will fail:
Error:
Executing SQL script in server
ERROR: Error 1005: Can't create table 'yet_another_test.book' (errno: 121)
However, if I change the engine of book or tag_book_item_relation to MyISAM, everything will be fine.
So, I am wondering what is going wrong if I use engine innodb for tablebook and tag_book_item_relation
The sql script is here(forward engineering in MySQL workbench):
CREATE TABLE IF NOT EXISTS `yet_another_test`.`tag` (
`id` INT NOT NULL AUTO_INCREMENT ,
PRIMARY KEY (`id`) ,
UNIQUE INDEX `id_UNIQUE` (`id` ASC) )
ENGINE = InnoDB;
CREATE TABLE IF NOT EXISTS `yet_another_test`.`book_item` (
`id` INT NOT NULL AUTO_INCREMENT ,
PRIMARY KEY (`id`) ,
UNIQUE INDEX `id_UNIQUE` (`id` ASC) )
ENGINE = InnoDB;
CREATE TABLE IF NOT EXISTS `yet_another_test`.`tag_book_item_relation` (
`book_item_id` INT NOT NULL ,
`tag_id` INT NOT NULL ,
PRIMARY KEY (`book_item_id`, `tag_id`) ,
INDEX `fk_tag` (`tag_id` ASC) ,
INDEX `fk_book_item` (`book_item_id` ASC) ,
CONSTRAINT `fk_tag`
FOREIGN KEY (`tag_id` )
REFERENCES `yet_another_test`.`tag` (`id` )
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `fk_book_item`
FOREIGN KEY (`book_item_id` )
REFERENCES `yet_another_test`.`book_item` (`id` )
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
CREATE TABLE IF NOT EXISTS `yet_another_test`.`book` (
`id` INT NOT NULL AUTO_INCREMENT ,
`book_item_id` INT NOT NULL ,
PRIMARY KEY (`id`, `book_item_id`) ,
INDEX `fk_book_item` (`book_item_id` ASC) ,
UNIQUE INDEX `id_UNIQUE` (`id` ASC) ,
CONSTRAINT `fk_book_item`
FOREIGN KEY (`book_item_id` )
REFERENCES `yet_another_test`.`book_item` (`id` )
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
It seems there is an issue with the "CREATE TABLE book" that the foreign key constraint fk_book_item has the same name as the constraint in tag_book_item_relation. Try using another name for the constraint in book and the CREATE TABLE should work fine.
This isn't a problem in MyISAM because they have no concept of foreign-keys and so the FK constraints are ignored.
Hope this helps!
Create your tables without the Foreign Key constraints. Although the same statements work with MyISAM engine, the constraints are silently ignored there - that is why you are not getting the errors. If you really need those constraints, then create them correctly. However, I generally tend to avoid FK constraints and implement the constraints at application level.
One problem I spot right away are the symbols of your constraints which have to be unique at DB level and you have fk_book_item both on tag_book_item_relation table and on book table
ERROR 121 says "Table creation failed because a foreign key constraint was not correctly formed. If the error message refers to error –1, table creation probably failed because the table includes a column name that matched the name of an internal InnoDB table."
Link
The Index Name and Constraint Name may be same, change that try creating the table.
This error message is saying that there is a duplicate key somewhere. It can be caused by a name conflict in a foreign key constraint; you cannot use the same foreign key name in different tables. (I don't know what other tables might be in your data base.)
Often, the error is caused by the table already existing in InnoDB's internal dictionary, even though the .frm file is gone. If that's the case, then the easiest thing to do is to do an sql dump of the data, drop the data base, recreate it, and then load the data from the dump.