Insert self-referencing data into copy table - mysql

I have a self-referencing foreign-key constraint on my table. Because I'm transforming the schema, I'd like to copy the existing data into a new table with (more or less) the same structure:
CREATE TABLE test(
id INT NOT NULL PRIMARY KEY,
parent INT,
FOREIGN KEY (parent) REFERENCES test(id)
);
CREATE TABLE copy(
id INT NOT NULL PRIMARY KEY,
parent INT,
FOREIGN KEY (parent) REFERENCES copy(id)
);
However, when inserting the data:
INSERT INTO copy(id, parent) SELECT id, parent FROM test;
MySQL gives me the common integrity error:
Error Code: 1452. Cannot add or update a child row:
a foreign key constraint fails (`test`.`copy`, CONSTRAINT `copy_ibfk_1`
FOREIGN KEY (`parent`) REFERENCES `copy` (`id`))
It seems that MySQL checks the constraint after inserting each row instead of checking it after the whole insert. The exact same example works fine in PostgreSQL.
Is there any other way to insert this data or am I stuck with doing this in two steps like this?
INSERT INTO copy(id) SELECT id FROM test;
UPDATE copy
JOIN test ON test.id = copy.id
SET copy.parent = test.parent;

You can use
SET FOREIGN_KEY_CHECKS=0;
before your INSERT and
SET FOREIGN_KEY_CHECKS=1;
after to disable the check of the foreign key constraint, so you don't have to worry about the order of the inserts:
See the documentation to foreign_key_checks
foreign_key_checks
If set to 1 (the default), foreign key constraints for InnoDB tables
are checked. If set to 0, such constraints are ignored. [...] Typically
you leave this setting enabled during normal operation, to enforce
referential integrity. Disabling foreign key checking can be useful
for reloading such tables in an order different from that required by
their parent/child relationships. See Section 14.6.6, “InnoDB and
FOREIGN KEY Constraints”. [...]
Note
Setting foreign_key_checks to 1 does not trigger a scan of the
existing table data. Therefore, rows added to the table while
foreign_key_checks = 0 will not be verified for consistency.
CREATE TABLE test(
id INT NOT NULL PRIMARY KEY,
parent INT,
FOREIGN KEY (parent) REFERENCES test(id)
);
CREATE TABLE copy(
id INT NOT NULL PRIMARY KEY,
parent INT,
FOREIGN KEY (parent) REFERENCES copy(id)
);
INSERT INTO test(id, parent) VALUES(1, null);
INSERT INTO test(id, parent) VALUES(2, 1);
INSERT INTO test(id, parent) VALUES(3, null);
INSERT INTO test(id, parent) VALUES(4, 2);
UPDATE test SET parent=3 WHERE id=1;
SET FOREIGN_KEY_CHECKS=0;
-- Success with MySQL
INSERT INTO copy(id, parent) SELECT id, parent FROM test;
SET FOREIGN_KEY_CHECKS=1;
Your updated fiddle: http://sqlfiddle.com/#!2/ae623/1

Related

MySQL Problem with foreign key on n:m relation

I have this database
create table ticket
(
id int auto_increment primary key,
name varchar(100)
);
create table document
(
id int auto_increment primary key,
name varchar(100)
);
create table ticket_document
(
ticket_id int,
document_id int
);
insert into ticket (id, name) VALUES (1, "a"),(2,"b");
insert into document (id, name) VALUES (1, "x"),(2,"y");
insert into ticket_document (ticket_id, document_id) VALUES (1,1),(1,2),(2,2);
So every ticket can have multiple documents and each document can be referenced to multiple tickets.
I want to achieve, that if I delete a ticket all references to his documents are deleted AND if there is no more reference to one of the document from an other ticket the document is also deleted.
But I don't know how to set the foreign keys.
Create foreign keys:
ALTER TABLE ticket_document
ADD FOREIGN KEY (ticket_id) REFERENCES ticket (id) ON DELETE CASCADE ON UPDATE CASCADE,
ADD FOREIGN KEY (document_id) REFERENCES document (id) ON DELETE CASCADE ON UPDATE CASCADE;
After ticket(s) deletion delete rows from document explicitly (or use service event procedure):
DELETE
FROM document
WHERE NOT EXISTS ( SELECT NULL
FROM ticket_document
WHERE ticket_document.document_id = document.id );
fiddle
Alternatively you may use AFTER DELETE trigger for auto-clearing document table:
CREATE TRIGGER tr_ad_ticket
AFTER DELETE
ON ticket
FOR EACH ROW
DELETE
FROM document
WHERE NOT EXISTS ( SELECT NULL
FROM ticket_document
WHERE ticket_document.document_id = document.id );
fiddle

How to update column with foreign key constraint within same table

In MySQL I want to update an ID-column of a table that is referenced in a foreign key constraint within that same table.
Example code:
CREATE TABLE A (
A_id int,
parent_A_id int,
PRIMARY KEY (A_id),
CONSTRAINT parent_A FOREIGN KEY (parent_A_id) REFERENCES A (A_id) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB;
INSERT INTO A VALUES (0, NULL), (1, 0);
Now I try to update the column A_id with
UPDATE A SET A_id = A_id + 1;
Unfortunately this throws an error:
Cannot delete or update a parent row: a foreign key constraint fails
I don't really understand why this fails and I can't find anything in the MySQL docs mentioning this not being allowed.
So, why does this not work?
And in the case that I don't just do a stupid mistake:
Is there any better way of doing such an update other than setting foreign_key_checks = 0 and doing everything by hand?
My problem with this would be that I'd like to use the ON UPDATE CASCADE to let the DB handle all foreign key updates for me.

Cannot add or update a child row: a foreign key constraint fails when trying to add a new row

I'm currently trying to add a new entry in a table. In this table I've a constraint to my users table (the user id). In some cases my user id can be not set yet. In the case it's empty, I'm getting an error:
Cannot add or update a child row: a foreign key constraint fails
This is how I setup my constraint:
So is there a way to insert an empty value to a constraint field? If no, what would be the best solution instead of removing the constraint?
So is there a way to insert an empty value to a constraint field?
Yes, there is. As long as the column controlled by the foreign key is not defined as NOT NULL, you can insert a NULL value into it, as explain in the manual. What is not allowed is to insert a non-NULL value that does not exists in the parent table (this includes the empty string!).
MySQL essentially implements the semantics defined by MATCH SIMPLE, which permit a foreign key to be all or partially NULL. In that case, the (child table) row containing such a foreign key is permitted to be inserted, and does not match any row in the referenced (parent) table.
Consider this demo on DB Fiddlde:
-- parent table
create table parent (id int primary key);
-- child table
create table child (
id int primary key,
parent_id int null, -- allow `NULL` values
constraint parent_id_fk foreign key(parent_id) references parent(id)
);
-- create a parent record
insert into parent(id) values(1);
-- insert a child record that references the parent: ok
insert into child(id, parent_id) values(1, 1);
-- insert a child record with a NULL parent_id : ok
insert into child(id, parent_id) values(2, NULL);
-- insert a child record with a (non-NULL) unknown parent_id
insert into child(id, parent_id) values(3, 2);
-- Error: Cannot add or update a child row: a foreign key constraint fails

MySQL Multi DELETE If relationship exists with foriegn key

I'm trying to (in a single statement) delete a row and all its relationships, even if all those relationships don't exist. Cascade on delete is not on option, and I would prefer to avoid subqueries.
Here is an example of what fails due to foreign key relationships:
CREATE TABLE test(id integer, title varchar(100), primary key(id));
INSERT into test(id, title) values(1, "Hello");
CREATE TABLE ref_a(id integer, test_id integer, primary key(id), key(test_id), constraint foreign key(test_id) references test(id));
INSERT into ref_a(id, test_id) values(1, 1);
CREATE TABLE ref_b(id integer, test_id integer, primary key(id), key(test_id), constraint foreign key(test_id) references test(id));
SET GLOBAL FOREIGN_KEY_CHECKS=1;
DELETE test, ref_a, ref_b FROM test
LEFT JOIN ref_a ON ref_a.test_id = test.id
LEFT JOIN ref_b ON ref_b.test_id = test.id
WHERE test.id = 1;
This fails with the error
Error Code: 1451. Cannot delete or update a parent row: a foreign key constraint fails (`product`.`ref_a`, CONSTRAINT `ref_a_ibfk_1` FOREIGN KEY (`test_id`) REFERENCES `test` (`id`))
Is this possible to do?
DB is InnoDb. MySql v 5.6.36
For your issue there are three options:
Enable ON DELETE CASCADE.
But that is not an option in your case apparently
Disable foreign_key_checks before running your query, and re-enable it afterwards
Run two queries; first deleting referencing rows (ref_a, ref_b), then the rows in test
Otherwise you this will not be possible, that's what foreign keys are for; to ensure data consistency.

if reforeign key is in itself

if the foreign key is in itself table, how to handle the first insertion problem.
/*外键是自己本身该如何处理插入问题*/
create table if not exists Course(
Cno varchar(10) primary key,
Cname varchar(100) not null,
Cpno varchar(10),
Ccredit tinyint,
foreign key (cpno) references course(cno)
);
/* the under sql will across error */
insert into Course(cno,cname,cpno,ccredit) value("1","数据库","5",4);
insert into Course(cno,cname,cpno,ccredit) value("2","数学",null,2);
insert into Course(cno,cname,cpno,ccredit) value("3","信息系统","1",4);
insert into Course(cno,cname,cpno,ccredit) value("4","操作系统","6",3);
insert into Course(cno,cname,cpno,ccredit) value("5","数据结构","7",4);
insert into Course(cno,cname,cpno,ccredit) value("6","数据处理",null,2);
insert into Course(cno,cname,cpno,ccredit) value("7","PASCAL语言","6",4);
enter image description here
how can I initialize the table course with Mysql?
Your first INSERT is this:
insert into Course(cno,cname,cpno,ccredit) value("1","数据库","5",4);
This attempts to create a row with value 5 in the column defined as the foreign key (FK), but without ensuring that a row with a cno value of 5 already exists. The FK constraint therefore refuses the insert.
You can fix this in one of three ways.
First, insert your rows in an order that insures the cno values exist before you use them by referring to them in cpno. I think this will be:
2, 6, 7, 5, 1, 3, 4
where you work out the order with a graph-traversal algorithm starting with root rows (rows with null cpno values).
Second, try turning off foreign key checking by giving this command SET FOREIGN_KEY_CHECKS=0; before your inserts. Give this command after your inserts to re-enable checking. SET FOREIGN_KEY_CHECKS=1;
Third, drop the foreign key constraint from the table before doing your inserts. Then add it back when you're done.
The second two methods generally apply only to bulk insertion operations you do on a quiet system. You probably don't want to disable foreign key checking in production, because it has value for data integrity assurance.
Read this. How to temporarily disable a foreign key constraint in MySQL?