MySQL parent child constraint - sql

I have this table which has a parent-child structure
CREATE TABLE `testable` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`parent_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
I now want to make sure every parent_id references a valid id.
Then I want to have an ON UPDATE CASCADE ON DELETE CASCADE on my id column so that if I change an id, every same parent_id gets updated automatically, and same for delete.
I've tried with the basic approach as follows:
ALTER TABLE `testable`
ADD CONSTRAINT `fk_testable_1`
FOREIGN KEY (`parent_id`)
REFERENCES `testable` (`id`)
ON DELETE CASCADE
ON UPDATE CASCADE;
I can then correctly try to update a row's parent_id with a non-existant id and the constraint blocks me.
The problem is then when I try to update an id which is referenced in some parent_id, there I get the following error:
ERROR 1451: 1451: Cannot delete or update a parent row: a foreign key constraint fails
The expected behaviour I want to achieve is my row id to change, and every row with that parent_id to update to reference the new id
Worth noting that parent_id is nullable and this is a table I use to create a dynamic side menu with sub-menus for my application.

Related

Error 1452 when updating a parent table using mariaDB

I get error 1452 when updating a row in a parent table when running mariaDB 10.5.8. The tables are defined as follows:
> SHOW CREATE TABLE files;
files CREATE TABLE `files` (
`file_path` varchar(255) NOT NULL,
`md5` text NOT NULL,
`size` int(11) NOT NULL,
PRIMARY KEY (`file_path`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
> SHOW CREATE TABLE headlines;
headlines CREATE TABLE `headlines` (
`file_path` varchar(255) NOT NULL,
`headline_offset` int(11) NOT NULL,
-- other columns
PRIMARY KEY (`file_path`,`headline_offset`),
CONSTRAINT `headlines_ibfk_1` FOREIGN KEY (`file_path`) REFERENCES `files` (`file_path`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
> SHOW CREATE TABLE headline_closures;
headline_closures CREATE TABLE `headline_closures` (
`file_path` varchar(255) NOT NULL,
`headline_offset` int(11) NOT NULL,
`parent_offset` int(11) NOT NULL,
`depth` int(11) DEFAULT NULL,
PRIMARY KEY (`file_path`,`headline_offset`,`parent_offset`),
KEY `file_path` (`file_path`,`parent_offset`),
CONSTRAINT `headline_closures_ibfk_1` FOREIGN KEY (`file_path`, `headline_offset`) REFERENCES `headlines` (`file_path`, `headline_offset`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `headline_closures_ibfk_2` FOREIGN KEY (`file_path`, `parent_offset`) REFERENCES `headlines` (`file_path`, `headline_offset`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
The contents of the database (minimal example):
> SELECT * FROM files;
/path/to/foo1.org 106e9f12c9e4ff3333425115d148fbd4 6
> SELECT * FROM headlines;
/path/to/foo1.org 1 --other columns
> SELECT * FROM headline_closures;
/path/to/foo1.org 1 1 0
Executing the following produces the error (expected behavior is that the file path changes for all tables per the CASCADE clauses):
> UPDATE files SET file_path='/path/to/foo2.org' WHERE md5='106e9f12c9e4ff3333425115d148fbd4';
ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (`org_sql`.`headline_closures`, CONSTRAINT `headline_closures_ibfk_2` FOREIGN KEY (`file_path`, `parent_offset`) REFERENCES `headlines` (`file_path`, `headline_offset`))
Things I've tried (per MySQL cascade update failed when foreign key column referenced in composite primary key)
Removing the second ON DELETE CASCADE ON UPDATE CASCADE from the headline_closures table (same error)
Dropping the primary key from the headline_closures table and that produced error 150 (Foreign key constraint is incorrectly formed)
I thought (1) would work because the docs state to not have multiple CASCADEs that could change a single column; I'm not sure why (2) would work. What am I not getting here?
Also, if someone could explain why this same arrangement works for SQLite and postgreSQL that would be appreciated since I don't get the same error with those ;)
Edit 1
This same behavior happens with MySQL 8.0.22 (so not just a mariaDB error, not too surprising)
I don't get the error if I take away one of the foreign keys in headline_closures (so there is still one constraint that has ON UPDATE CASCADE)
If I remove the ON UPDATE CASCADE from headline_closures_ibfk_1 instead of headline_closures_ibfk_2, the error then complains about headline_closures_ibfk_1 (where it complained about headline_closures_ibfk_2 when I took the CASCADEs away from headline_closures_ibfk_2 or if I left them on both) so it seem that the error is due to updating the parent, having the cascade propagate to the headline_closures table where the second constraint doesn't see the update and therefore complains (or something)
Edit 2:
DELETE FROM files WHERE md5='106e9f12c9e4ff3333425115d148fbd4'; works as intended with the constraints as written above (no error and the the deletion propagates to all child tables).

How to add delete cascade constraint in sql

I have 2 tables.Table A have columns as (aid, name,depart) where aid is primary key.
Table B has (aid1,aid2,aid3,created_by) where aid1 is the primary_key. aid1, aid2 and aid3 all are primary key of Table A
I want to delete a record in Table B i.e aid1 and simultaneously with delete cascade all three records in TABLE A should be deleted. My doubt here is where should I put the delete cascade constraint. I know that in parent child relationship we need to put delete cascade on the child table so that when parent is deleted, child entities are also deleted but in this scenario I dont understand where I should put delete cascade
Cascading a table will be on the child table. You have to set this on your child table when creating or after creating the child table.
E.g
CREATE TABLE schools (
id int auto_increment primary key not null,
schoolname varchar(191) not null
);
CREATE TABLE students(
id int auto_increment primary key not null,
studentname varchar(191) not null,
school_id int FOREIGN KEY REFERENCES students(id) ONDELETE CASCADE
);
or
You can as well alter the table by running this command.
ALTER TABLE childTable
ADD FOREIGN KEY (childTableParentTableColumn) REFERENCES parentTable(parentTableColumn);

Create three tables with the same auto increment primary key and name

Cannot find a good answer for this online. I need to create three tables as an example below, a parent with an auto increment ID that will then link to the two child tables (Subject and Comment) with the same exact ID and cascade back if that parent ID is deleted.
Any ideas on how to solve?
I have googled and am extremely confused as to how to solve this one. I have a decent amount of experience with SQL, but not with creating tables and relationships.
CREATE TABLE Parent
(
ParentID INT NOT NULL IDENTITY PRIMARY KEY,
Email VARCHAR(50) NOT NULL,...
)
CREATE TABLE Subject
(
ParentID INT NOT NULL PRIMARY KEY,
Subject
)
CREATE TABLE Comment
(
ParentID INT NOT NULL PRIMARY KEY,
Comment VARCHAR(100)
)
Use a 1 to 1 relationship with on delete cascade:
CREATE TABLE Parent(
ParentID INT NOT NULL IDENTITY PRIMARY KEY,
Email VARCHAR(50) NOT NULL,...
)
CREATE TABLE Subject(
ParentID INT NOT NULL PRIMARY KEY,
Subject,
CONSTRAINT fk_SubjectParentId FOREIGN KEY (ParentID)
REFERENCES Parent (ParentID) ON DELETE CASCADE
)
CREATE TABLE Comment(
ParentID INT NOT NULL PRIMARY KEY,
Comment VARCHAR(100),
CONSTRAINT fk_CommentParentId FOREIGN KEY (ParentID)
REFERENCES Parent (ParentID) ON DELETE CASCADE
)
This is known as a 1 to 1 relationship, since both ends of the foreign key are unique within their table.
Though I have to agree with Mitch Wheat comment, cascading deletes is something to use with caution. by specifying cascade delete, you are telling the database engine to delete the related records whenever a parent record is deleted. Not having that cascade delete option will simply throw an error if you attempt to delete a record that is referenced by another table. This forces you, as a developer, to think about the side effects of deleting rows from the parent table and basically acts as a "Are you sure you want to delete?" guard against unwanted deletes.

Restrict delete of master record if it has children

How can I restrict delete of a master record if it has children in a Firebird database.
You need to add a foreign key from the child table to the parent table. The default behavior of a foreign key will prevent deletion of records from the parent table if there are child records.
For example
create table parent (
id integer generated by default as identity primary key
);
create table child (
id integer generated by default as identity primary key,
parent_id integer references parent(id)
);
This will also prevent you from adding records to child with parent_id values (other than null) that do not exist in parent. You can further modify the behavior of the foreign key constraint using on update and on delete clauses. See the Firebird language reference on constraints. For example using on delete cascade will delete rows from the child table if the parent record is deleted.
A foreign key can only point to a primary key or a unique key.
The above code is intentionally short, you should consider using named constraints for both primary and foreign key constraints, as it will simplify future maintenance, check the language reference for details.

Why is this a cyclical foreign key constraint?

I came upon this code, marked "error," in an application I'm to update. Running it on a test database gives a cyclical reference error:
The referential relationship will result in a cyclical reference that is not allowed (Constraint name = descriptions_fk_2)
I named the constraints to see which one caused the problem.
CREATE TABLE items (
id INT NOT NULL UNIQUE IDENTITY,
name NCHAR(100) NOT NULL UNIQUE,
PRIMARY KEY (id)
);
CREATE TABLE sources (
id INT NOT NULL UNIQUE IDENTITY,
item_id INT NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (item_id)
REFERENCES items(id) ON UPDATE NO ACTION ON DELETE CASCADE
);
CREATE TABLE descriptions (
id INT NOT NULL UNIQUE IDENTITY,
item_id INT NOT NULL,
source_id INT NOT NULL,
PRIMARY KEY (id),
CONSTRAINT descriptions_fk_1 FOREIGN KEY (item_id)
REFERENCES items(id) ON UPDATE NO ACTION ON DELETE CASCADE,
CONSTRAINT descriptions_fk_2 FOREIGN KEY (source_id)
REFERENCES sources(id) ON UPDATE NO ACTION ON DELETE CASCADE
);
Why is this a cyclical reference? The descriptions table is linked to two separate tables, but none of them link back to descriptions.
It's not strictly cyclical - but there are multiple cascade paths. So you could cascade delete a row in items two ways:
1) description -> item
2) description -> source -> item
And, for that reason, it's disallowed.
I believe it's a performance concern, as PostGres will allow cycles like that and will just work it out, but deletes under those circumstances can be quite slow.
For some further reading about why it's disallowed, please see this answer.