SQL - How do I delete two entities referencing each other - sql

So I have two entities referencing each other, parent, child.
child must be deleted if parent is deleted, but cannot be deleted while there's still a parent referencing it.
These are the two constraints I've been given:
ALTER TABLE public.parent
ADD CONSTRAINT parent__child_id__fk
FOREIGN KEY (child_id) REFERENCES child(id)
ON DELETE CASCADE
;
ALTER TABLE public.child
ADD CONSTRAINT child__parent_code__id__fk
FOREIGN KEY (parent_code, id) REFERENCES parent(code, child_id)
ON UPDATE CASCADE
ON DELETE RESTRICT
DEFERRABLE INITIALLY DEFERRED
;
I now want to delete a parent (and the corresponding child) ...
SQL Error [23503]:
ERROR: update or delete on table "parent" violates foreign key constraint
"child__parent_code__id__fk" on table "child"
Detail: Key (code, child_id)=(A0B7EBF6-3_DELETE_ME, 10)
is still referenced from table "child".
Whoop-dee-doo ...
Yes, it's referenced by the bloody entry I'm trying to delete...
(which I know because there's a unique constraint on parent.code)
Looks like I CAN delete the entry if I set the child's fk to ON DELETE CASCADE, but that doesn't seem to be what the guy breathing down my neck wants, which is "if you delete a parent delete its child, too, if you delete a child that has a parent, DON'T".
How do I achieve this?

Delete from both tables in one statement using a CTE:
WITH x AS (
DELETE FROM parent
WHERE ...
RETURNING id
)
DELETE FROM child
WHERE ...;

Related

Two FKs pointing to same parent column - ON UPDATE CASCADE - SQL Server

Here's the scenario.
- Parent Table: TEAMMEMBERS, with a primary key RecID
- Child Table: TEAMMEMBERTASKS
I have two columns in the TEAMMEMBERTASKS table, ReportedBy and AssignedTo.
Both of these columns use the RecID to store which team member reported a task and which team member the task is assigned to. The RecID could be the same for both columns, but that is not always the case.
I need to add in a FK for both child columns that check the relationship to the parent, and I would like to add ON UPDATE CASCADE to both of the foreign keys.
Whenever I try to do this, my second foreign key throws a 'may cause cycles or multiple cascade paths' error.
Here's my code:
ALTER TABLE [dbo].[TEAMMEMBERTASKS] WITH CHECK ADD CONSTRAINT
[FK_AssignedTo_TeamMemberRecID] FOREIGN KEY([AssignedTo])
REFERENCES [dbo].[TEAMMEMBERS] ([RecID])
GO
ALTER TABLE [dbo].[TEAMMEMBERTASKS] CHECK CONSTRAINT
[FK_AssignedTo_TeamMemberRecID]
GO
ALTER TABLE [dbo].[TEAMMEMBERTASKS] WITH CHECK ADD CONSTRAINT
[FK_ReportedBy_TeamMemberRecID] FOREIGN KEY([ReportedBy])
REFERENCES [dbo].[TEAMMEMBERS] ([RecID])
ON UPDATE CASCADE
GO
ALTER TABLE [dbo].[TEAMMEMBERTASKS] CHECK CONSTRAINT
[FK_ReportedBy_TeamMemberRecID]
GO
With the current code, will this cause the RecID to be updated in both child columns or will it cause the update command to be restricted?
Should I just go ahead and write up a trigger that deals with this instead?

How to delete records from parent table which is referenced by multiple child tables?

I have a table which is referenced by multiple tables (around 52) and further,few of the child tables have multiple foreign keys also that is referencing other tables too.
I want to delete a record from parent table, I am unable to do so, as I am getting error "The DELETE statement conflicted with the REFERENCE constraint "FK_xxx". The conflict occurred in database "MyDB", table "dbo.A", column 'x'."
I want a generalized T-SQL solution which is irrespective of tables and number of references.
You have to look at the "on delete" keyword which is a part of the foreign key constraint definition.
Basically you have 4 options:
NO ACTION (does nothing)
CASCADE (deletes the child aswell)
SET NULL (sets the reference field to null)
SET DEFAULT (sets the reference field to the default value)
An example would be:
CREATE TABLE parent (
id INT NOT NULL,
PRIMARY KEY (id)
) ENGINE=INNODB;
CREATE TABLE child (
id INT,
parent_id INT,
INDEX par_ind (parent_id),
FOREIGN KEY (parent_id)
REFERENCES parent(id)
ON DELETE CASCADE -- replace CASCADE with your choice
) ENGINE=INNODB;
(for this example and more details look here: http://dev.mysql.com/doc/refman/5.7/en/create-table-foreign-keys.html )
If you now want to modify your constraint, you first have to drop it, and create a new one like for example:
ALTER TABLE child
ADD CONSTRAINT fk_name
FOREIGN KEY (parent_id)
REFERENCES parent(id)
ON DELETE CASCADE; -- replace CASCADE with your choice
I hope this helped. Also to mention it, you should think about maybe not really deleting your parent, and instead creating another boolean column "deleted", which you fill with "yes" if someone clicks the delete. In the "Select"-query you filter then by that "deleted" column.
The advantage is, that you do not lose the history of this entry.
Your problem is this: A FK constraint is designed to prevent you from creating an orphaned child record in any of the 52 tables. I can provide you with the script you seek, but you must realise first that when you try to re-enable the FK constraints the constraints will fail to re-enable because of the orphaned data (which the FK constraints are designed to prevent). For your next step, will have to delete the orphaned data in each of the 52 tables first anyway. It is actually much easier just to redo the constraints with ON DELETE CASCADE, or drop the constraints and forget about referential integrity altogether. You can't have it both ways.

Very slow SQL DELETE query on table with foreign key constraint

I have got some trouble with a SQL DELETE query.
I work on a database (postgres 9.3) with 2 tables (Parent and Child).
The child has a relation to the parent with a foreign key.
Parent Table
CREATE TABLE parent
(
id bigint NOT NULL,
...
CONSTRAINT parent_pkey PRIMARY KEY (id)
)
Child Table
CREATE TABLE child
(
id bigint NOT NULL,
parent_id bigint,
...
CONSTRAINT child_pkey PRIMARY KEY (id),
CONSTRAINT fk_adc9xan172ilseglcmi1hi0co FOREIGN KEY (parent_id)
REFERENCES parent (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
I inserted in both tables 200'000 entries without any relation ( Child.parent_id = NULL).
But a DELETE query like below has a duration of more than 20 minutes.
And that even without a WHERE conditions.
DELETE FROM Parent;
If I don't add the relation constraints the execution time will be done in 400 ms.
What did I miss?
A workable solution is the example below. But I don't know if this is a good idea. Maybe anyone could tell me a better way to do that.
BEGIN WORK;
ALTER TABLE Parent DISABLE TRIGGER ALL;
DELETE FROM Parent;
ALTER TABLE Parent ENABLE TRIGGER ALL;
COMMIT WORK;
When you delete from Parent, the Child table needs to be queried by parent_id to ensure that no child row refers to the parent row you are about to delete.
To ensure that the child lookup runs quickly, you need to have an index on your parent_id column in the Child table.

How to delete a row ONLY in parent table, which is referenced by a Foregin Key from the child table

I want to delete a row/tuple from a parent table, but it is throwing an error message because it has a FOREIGN KEY reference in its child table.
However, in my case I want to delete the record only from the parent table and maintain the data in the child table.
Is it possible to achieve this?
I know the usage of ON DELETE CASCADE, but I want to know if there is a solution for the secenario I described?
It is possible with some agreements in your data. To maintain child table data you'll have to do ON DELETE SET NULL. This will leave data, but set FK to NULL value (in child table). And that is because of data-integrity: while you can keep your data, your FK can not refer to non-existent row of parent table in terms of enforcing FK constraint. Thus, it will be set to NULL by this.
If you want to "save" value of FK - then you definitely should not use FK at all because such behavior violates what FK is. So then just don't use that constraint, but be aware of possible integrity fails.
The point of a foreign key constraint is to prevent orphan records in the child table. So, no, it's not possible to do that, unless you drop the foreign key relationship.
If you rely on 'ON DELETE CASCADE', then deleting the parent record will result in all the corresponding children to be deleted.
If you want to delete the parent, but keep the children, you need to drop the foreign key constraint, or set the constraint to be 'ON DELETE SET NULL'. If you set 'ON DELETE SET NULL', then when you delete the parent record, the child records will remain, but the foreign key column value will be set to NULL.
delete a row ONLY in parent table, which is referenced by a Foregin Key from the child table
If Multiple table has been mapped in one table in that case all foreign key i.e :-
$table->integer('customer_id')->unsigned()->nullable();
$table->foreign('customer_id')->references('id')
->on('customers')->onDelete(`SET NULL`);

SQL Server 2005 foreign key cascade

I have a table ParentTable (id, Name, town) and its child table human (mother, father) in which both columns contain mappings to the parent table.
The parent table has the column town which contain foreign key reference with Town (townid, townname) and that town is added with foreign key reference.
Now the problem is I need to give the cascade constraint for human table, but in SQL Server multiple column for the same parent table can not be given cascade. So I created trigger like this,
create trigger DEL_Parent
ON Parent
instead of delete as
set nocount on
delete from human
where mother IN (select id from deleted)
or father IN (select id from deleted)
delete from Parent where id in(select id from deleted).
But when I try to execute the trigger, the system shows the following error
Cannot create instead of delete or instead of update trigger
DEL_Parent on table Parent. This is because the table has a foreign
key with cascading delete or update.
How could resolve the problem? Thanks in advance.
Modify the query as:
create trigger DEL_Parent
ON Parent
after delete as
set nocount on
delete
from human
where mother IN (select id from deleted)
or father IN (select id from deleted)
delete
from Parent
where id in(select id from deleted)
because INSTEAD OF DELETE triggers cannot be defined on a table that has a foreign key defined with a DELETE action.
INSTEAD OF DELETE triggers cannot be defined on a table that has a foreign key defined with a DELETE action.
Source: http://msdn.microsoft.com/en-us/library/ms191208(v=sql.90).aspx
So, I guess if you wanted to you would have to drop your cascade delete and handle that with a trigger if you still wanted to enforce it.