Why is ON DELETE CASCADE not removing referenced record? - mysql

I've created a SQLFiddle with the code shown below. The problem is that after the DELETE statement the associated credit_card record is supposed to be deleted as well.
CREATE TABLE person (
id BIGINT AUTO_INCREMENT PRIMARY KEY
);
CREATE TABLE credit_card (
id BIGINT AUTO_INCREMENT PRIMARY KEY
);
CREATE TABLE person_credit_card (
person_id BIGINT NOT NULL,
credit_card_id BIGINT NOT NULL UNIQUE, -- Please note that this is UNIQUE
PRIMARY KEY(person_id, credit_card_id),
CONSTRAINT fk__person_credit_card__person
FOREIGN KEY (person_id)
REFERENCES person(id),
KEY pkey (credit_card_id),
CONSTRAINT fk__person_credit_card__credit_card
FOREIGN KEY (credit_card_id)
REFERENCES credit_card(id)
ON DELETE CASCADE
ON UPDATE CASCADE
);
INSERT INTO person (id) VALUES (1);
INSERT INTO credit_card (id) VALUES (1);
INSERT INTO person_credit_card (person_id, credit_card_id) VALUES (1, 1);
DELETE FROM person_credit_card WHERE credit_card_id = 1;
I'm not sure why this is not working. With the UNIQUE constraint on the credit_card_id this is not possible:
+--------------------------------------+
| person_credit_card |
+--------------------------------------+
| person_id | credit_card_id |
+--------------------------------------+
| 1 | 1 |
+--------------------------------------+
| 2 | 1 |
+--------------------------------------+
So what am I doing wrong here and how can I make it work?
I also tried to e.g. delete a person and remove all his credit_card records (see this other SQLFiddle):
CREATE TABLE person (
id BIGINT AUTO_INCREMENT PRIMARY KEY
);
CREATE TABLE credit_card (
id BIGINT AUTO_INCREMENT PRIMARY KEY
);
CREATE TABLE person_credit_card (
person_id BIGINT NOT NULL,
credit_card_id BIGINT NOT NULL UNIQUE,
PRIMARY KEY(person_id, credit_card_id),
CONSTRAINT fk__person_credit_card__person
FOREIGN KEY (person_id)
REFERENCES person(id)
ON DELETE CASCADE
ON UPDATE CASCADE,
CONSTRAINT fk__person_credit_card__credit_card
FOREIGN KEY (credit_card_id)
REFERENCES credit_card(id)
ON DELETE CASCADE
ON UPDATE CASCADE
);
INSERT INTO person (id) VALUES (1);
INSERT INTO person (id) VALUES (2);
INSERT INTO credit_card (id) VALUES (1);
INSERT INTO credit_card (id) VALUES (2);
INSERT INTO credit_card (id) VALUES (3);
INSERT INTO person_credit_card (person_id, credit_card_id) VALUES (1, 1);
INSERT INTO person_credit_card (person_id, credit_card_id) VALUES (1, 2);
INSERT INTO person_credit_card (person_id, credit_card_id) VALUES (2, 3);
DELETE FROM person WHERE id = 1;
but the outcome is that only the resolution table is losing its entries but the credit_card records are still there.

From the documentation:
CASCADE: Delete or update the row from the parent table, and
automatically delete or update the matching rows in the child table.
Both ON DELETE CASCADE and ON UPDATE CASCADE are supported.

Deleting from person_credit_card won't cascade person nor credit_card.
Cascade works by deleting/updating records from tables that reference the record being deleted.
In other words, since person doesn't have a column with reference to person_credit_card, then it won't be deleted.

Related

MySQL doesn't prevent deletion of rows where primary key is associated with another table

tested this on postgreSQL and it doesn't let me delete any row(primary key) associated with another table. Is MySQL different or is there is a way to do that here in Mysql?.
CREATE TABLE account (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(30)
);
CREATE TABLE transaction (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
account_id BIGINT REFERENCES account(id)
);
INSERT INTO account (name) VALUES ('john'); //account_id = 1
INSERT INTO transaction (account_id) VALUES (1);
DELETE FROM account where id = 1;
);
when I delete an account row in account table, mySQL doesn't prevent the deletion of it.
Here is the example for MYSQL. You need add the foreign key with update and delete constrains. You can also read through the documents for more details. Foreign Key
CREATE TABLE account (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(30)
);
CREATE TABLE transaction (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
account_id BIGINT,
FOREIGN KEY (account_id) REFERENCES account(id) ON UPDATE CASCADE ON DELETE RESTRICT
);
INSERT INTO account (name) VALUES ('john');
INSERT INTO transaction (account_id) VALUES (1);
DELETE FROM account where id = 1;
-- 18:32:14 DELETE FROM account where id = 1 Error Code: 1451. Cannot delete or update a parent row: a foreign key constraint fails (`test`.`transaction`, CONSTRAINT `transaction_ibfk_1` FOREIGN KEY (`account_id`) REFERENCES `account` (`id`) ON UPDATE CASCADE) 0.039 sec

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

1 auto_increment with 2 primary keys

My intention is to create a table with 2 primary keys with one of those autoincrementing and the other specified when inserting and when I create a new field for this table it must start the recount if the not incremented primary key changes. This is what I had:
I have been able to get this changing the table engine to MyISAM. But there is something missing, the auto_increment does not start at 100 as it happened before.
CREATE TABLE CONFIGURABLES(
CODIEI2 INTEGER AUTO_INCREMENT,
CODIEI1 INTEGER,
SKU VARCHAR(30),
COLOR INTEGER,
COLOR2 INTEGER,
TALLA INTEGER,
CONSTRAINT PK_CODIEI PRIMARY KEY(CODIEI1,CODIEI2),
CONSTRAINT FK_CODIEI1 FOREIGN KEY(CODIEI1) REFERENCES PRODUCTOS(ENTITY_ID) ON DELETE CASCADE,
CONSTRAINT FK_CCOLOR FOREIGN KEY(COLOR) REFERENCES COLORES(CODICOL) ON DELETE CASCADE,
CONSTRAINT FK_CCOLOR2 FOREIGN KEY(COLOR2) REFERENCES COLORES(CODICOL) ON DELETE CASCADE,
CONSTRAINT FK_CTALLA FOREIGN KEY(TALLA) REFERENCES TALLAS(CODITLL) ON DELETE CASCADE) ENGINE=MyISAM;
ALTER TABLE CONFIGURABLES AUTO_INCREMENT = 100;
Is this happening because when the auto_increment value is different from the default number the engine must be set to InnoDB?
Is there a way to get it as I want?
SOLUTION:
The table can be back to InnoDB which is much better and there is no need for auto_increment on CONFIGURABLES table as this will be controlled when doing the insert.
CREATE TABLE CONFIGURABLES(
CODIEI2 INTEGER,
CODIEI1 INTEGER,
SKU VARCHAR(30),
COLOR INTEGER,
COLOR2 INTEGER,
TALLA INTEGER,
CONSTRAINT PK_CODIEI PRIMARY KEY(CODIEI1,CODIEI2),
CONSTRAINT FK_CODIEI1 FOREIGN KEY(CODIEI1) REFERENCES PRODUCTOS(ENTITY_ID) ON DELETE CASCADE,
CONSTRAINT FK_CCOLOR FOREIGN KEY(COLOR) REFERENCES COLORES(CODICOL) ON DELETE CASCADE,
CONSTRAINT FK_CCOLOR2 FOREIGN KEY(COLOR2) REFERENCES COLORES(CODICOL) ON DELETE CASCADE,
CONSTRAINT FK_CTALLA FOREIGN KEY(TALLA) REFERENCES TALLAS(CODITLL) ON DELETE CASCADE);
And when doing the insert do this:
BEGIN;
SELECT #id := IFNULL(MAX(CODIEI2)+1,100) FROM CONFIGURABLES WHERE CODIEI1 = 10001 FOR UPDATE;
INSERT INTO CONFIGURABLES
(CODIEI1,CODIEI2,SKU,COLOR,COLOR2,TALLA)
VALUES
(10001,#id,'',4,2,2);
COMMIT;
BEGIN;
SELECT #id := MAX(id)+1 FROM foo WHERE other = 123 FOR UPDATE;
INSERT INTO foo
(other, id, ...)
VALUES
(123, #id, ...);
COMMIT;
How do you insert your data? If you give CODIEI2 as a column and a value as well, this will overwrite the auto_increment.
Test in SQLFiddle:
Build schema:
CREATE TABLE CONFIGURABLES(
CODIEI2 INTEGER AUTO_INCREMENT,
CODIEI1 INTEGER,
CONSTRAINT PK_CODIEI PRIMARY KEY(CODIEI2));
ALTER TABLE CONFIGURABLES AUTO_INCREMENT = 100;
INSERT INTO CONFIGURABLES ( CODIEI1 )
VALUES ( 1 );
Now overwriting the auto_increment:
INSERT INTO CONFIGURABLES ( CODIEI2, CODIEI1 )
VALUES ( 1, 2 );
Run SQL:
SELECT *
FROM CONFIGURABLES;
Output:
CODIEI2 CODIEI1
1 2
100 1

Fail to trigger BEFORE INSERT INTO a table that only has one auto increment column

I'm planning to make a notification system that sends out different types of messages to users (private messages among users, message about their posts being published etc) What I'm looking for is some sort of conditional foreign key on the column this_id in notification table so that it can find a specific row from either table comments or pm, and I have found this thread that's exactly what I want. So I create a table "supertable" with just one column (SERIAL PRIMARY KEY) to be referenced by comments and PM. And to make sure a new row is insert into the supertable first to generate a key before any new row inserts into either comments and PM, I set up two BEFORE INSERT INTO triggers
CREATE TRIGGER `before_insert_comments`
BEFORE INSERT ON `comments`
FOR EACH ROW BEGIN
INSERT INTO supertable (this_id) VALUES ('')
END;
CREATE TRIGGER `before_insert_pm`
BEFORE INSERT ON `PM`
FOR EACH ROW BEGIN
INSERT INTO supertable (this_id) VALUES ('')
END;
But when inserting a record into table comments or PM, I'm still getting the error
Cannot add or update a child row: a foreign key constraint fails ( CONSTRAINT comments FOREIGN KEY (this_id) REFERENCES supertable (this_id) ON DELETE CASCADE ON UPDATE CASCADE). Anyone know what's the problem with the triggers?
Table schema
CREATE TABLE notification (
id INT NOT NULL AUTO_INCREMENT,
this_id SERIAL PRIMARY KEY,
user_id INT,
is_read TINYINT,
FOREIGN KEY (this_id) REFERENCES supertable(this_id)
ON UPDATE CASCADE
ON DELETE CASCADE
);
CREATE TABLE supertable (
this_id SERIAL PRIMARY KEY
);
CREATE TABLE comments (
this_id SERIAL PRIMARY KEY,
user_id INT ,
post TEXT,
is_approved TINYINT,
...
FOREIGN KEY (this_id) REFERENCES supertable(this_id)
ON UPDATE CASCADE
ON DELETE CASCADE
);
CREATE TABLE PM (
this_id SERIAL PRIMARY KEY,
sender_id INT,
recipient_id INT ,
msg TEXT,
...
FOREIGN KEY (this_id) REFERENCES supertable(this_id)
ON UPDATE CASCADE
ON DELETE CASCADE
);
Check again your column mapping. Few key to point here.
your notification table primary key should set to id instead of this_foreign_id(I rename it to avoid confuse).
datatype for this_foreign_id should be BIGINT UNSIGNED to map with SERIAL, since 1 table only can have 1 auto increment column and it should be primary key.
on your trigger, insert null instead of '' cause SERIAL column is meant for BIGINT UNSIGNED
Let me know if it work.
CREATE TABLE supertable (
this_id SERIAL PRIMARY KEY
);
CREATE TABLE notification (
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
this_foreign_id BIGINT UNSIGNED,
user_id INT,
is_read TINYINT,
FOREIGN KEY (this_foreign_id) REFERENCES supertable(this_id)
ON UPDATE CASCADE
ON DELETE CASCADE
);
CREATE TABLE comments (
this_id SERIAL PRIMARY KEY,
user_id INT ,
post TEXT,
is_approved TINYINT,
FOREIGN KEY (this_id) REFERENCES supertable(this_id)
ON UPDATE CASCADE
ON DELETE CASCADE
);
CREATE TABLE PM (
this_id SERIAL PRIMARY KEY,
sender_id INT,
recipient_id INT ,
msg TEXT,
FOREIGN KEY (this_id) REFERENCES supertable(this_id)
ON UPDATE CASCADE
ON DELETE CASCADE
);
CREATE TRIGGER before_insert_comments
BEGIN
BEFORE INSERT ON comments
FOR EACH ROW BEGIN
INSERT INTO supertable (this_id) VALUES (NULL);
END;
CREATE TRIGGER before_insert_pm
BEGIN
BEFORE INSERT ON PM
FOR EACH ROW BEGIN
INSERT INTO supertable (this_id) VALUES (NULL);
END;

Mysql mapping table FK constraints

I'm experiencing some difficulties with foreign key constraints. I have three tables:
Features
ID PK AUTO_INC
Title VARCHAR
Subscriptions
ID PK AUTO_INC
Title VARCHAR
Subscriptionfeatures
ID PK AUTO_INC
feature_id INT (index)
subscription_id INT (index)
When I have the following records
Features
1 Testfeature
Subscriptions
1 Testsubscription
I can insert the following record in Subscriptionfeatures when defining a FK constraint as follows
ALTER TABLE subscriptionfeatures ADD CONSTRAINT FK_feature FOREIGN KEY (feature_id) REFERENCES features(id);
Subscriptionfeatures
x 1 1 => ok
But I can not insert the identical record when adding an ON DELETE CASCADE clause to the FK constraint, but i must admint i do not understand its reason for denial!
ALTER TABLE subscriptionfeatures ADD CONSTRAINT FK_feature FOREIGN KEY (feature_id) REFERENCES features(id) ON DELETE CASCADE;
Subscriptionfeatures
x 1 1 => fails
Any help on this would be greatly appreciated!
This works for me:
CREATE TABLE Features (
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
Title VARCHAR(100)
) Engine=InnoDB;
CREATE TABLE Subsriptions (
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
Title VARCHAR(100)
) Engine=InnoDB;
CREATE TABLE Subscriptionfeatures (
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
feature_id INT NOT NULL,
subscription_id INT NOT NULL,
KEY ix_subscriptionfeatures_feature (feature_id),
KEY ix_subscriptionfeatures_subscription (subscription_id)
) Engine=InnoDB;
INSERT
INTO Features
VALUES (1, 'Testfeature');
INSERT
INTO Subsriptions
VALUES (1, 'Testsubscription');
ALTER TABLE subscriptionfeatures ADD CONSTRAINT FK_feature FOREIGN KEY (feature_id) REFERENCES features(id) ON DELETE CASCADE;
INSERT
INTO Subscriptionfeatures (feature_id, subscription_id)
VALUES (1, 1);
What is the error the MySQL gives?