DELETE statement conflicted in Database First - sql

Let's say I have a database with table A and table B. B has a foreign key to table A, which does not allow nulls. When I try to delete and entity of A I want all references in table B to be removed as well. I try to do this with the following code:
using (var ctx = new MyDatabaseContext(ConnectionString))
{
var a= new A() { IdA= idA};
ctx.A.Attach(a);
ctx.A.Remove(a);
ctx.SaveChanges();
}
This results in the following error message:
Additional information: The DELETE statement conflicted with the REFERENCE constraint "FK_B_A". The conflict occurred in database "MyDatabase", table "dbo.B", column 'IdA'.
The statement has been terminated.
I have tried a lot, from using Triggers in the database to defining the ON DELETE CASCADE, but Entity Framework does fail. What am I doing wrong?
Trigger:
ALTER TRIGGER [dbo].[trg_DelA]
ON [dbo].[A]
FOR DELETE AS
BEGIN
DELETE FROM B WHERE B.IdA = IdA;
END
BTW: This is just an example. The actual database is larger and also contains intermediate tables for many to many relationships.
BR
Thomas

AFTER (or FOR - they are synonyms) trigger are fired after triggering SQL statement. In your case this is too late, since deleting statement can't be completed due to foreign keys.
If you want to use trigger to handle cascade deletion - you have to use instead of trigger, and in this trigger first delete records from B table and then from A table.
So this could look like:
CREATE TRIGGER [dbo].[trg_DelA]
ON [dbo].[A]
INSTEAD OF DELETE AS
BEGIN
DELETE FROM B WHERE B.IdA in (select IdA from deleted)
DELETE FROM A WHERE IdA in (select IdA from deleted)
END
See MSDN for reference.

Cascade your deletes.
Take a look at this: Entity framework code first delete with cascade
And this: https://msdn.microsoft.com/en-us/library/hh295843(v=vs.103).aspx

Related

Delete trigger to delete child records

So, I have two tables.
One is sales_order (no_so VARCHAR PRIMARY KEY)
The other is status_so (no_so REFERENCES to sales_order)
I want the no_so from status_so is deleted if I delete the no_so in sales_order. So, I create the trigger to solve the problem.
CREATE TRIGGER trg_sales_order
ON sales_order
FOR DELETE
AS
DELETE FROM status_so
WHERE status_so.no_so IN (SELECT deleted.no_so FROM deleted)
after that I run this code
DELETE FROM sales_order WHERE no_so = 'SO004'
and I still got an error, it says
The DELETE statement conflicted with the REFERENCE constraint "FK_status_so_sales_order". The conflict occurred in database "db", table "dbo.status_so", column 'no_so'.
From the docs:
OR | AFTER FOR or AFTER specifies that the DML trigger fires only when
all operations specified in the triggering SQL statement have launched
successfully. All referential cascade actions and constraint checks
must also succeed before this trigger fires.
You need to create instead of delete trigger and handle the deletions from the both table in the correct order there.

2 foreign key to the same table may cause cycles or multiple cascade paths

Merry Christmas,
I'm looking to find a alternative database design to avoid this ULTRA SAFE error:
Introducing FOREIGN KEY constraint '%1' on table '%2' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
When 2 fields of the same table refer to the same FK. Eg:
People: {Id, Name, Age, Gender}
FamilyRelative: {PersonId, FamiliarId, Relationship}
FK: FamilyRelative.PersonId ->People.Id ON DELETE CASCADE ON UPDATE CASCADE
FK: FamilyRelative.FamiliarId->People.Id ON DELETE CASCADE ON UPDATE CASCADE
Throws an error on the second FK.
PS: I'm testing it in SQL Server 2008R2
You could remove the DELETE CASCADE Action on the FamilyRelative.FamiliarId
And then when you want to delete a People record first you
DELETE FROM FamilyRelative
WHERE FamiliarId = #PeopleId
and after that you
DELETE FROM People
WHERE Id = #PeopleId
The last delete will take care of the remaining FamilyRelative records with FamilyRelative.PeopleId = #PeopleId using the CASCADE rule
You can handle this in an INSTEAD OF trigger, so when you try and delete from dbo.People, you can perform the necessary delete on dbo.FamilyRelation first to avoid any integrity errors:
CREATE TRIGGER dbo.People_Delete ON dbo.People
INSTEAD OF DELETE
AS
BEGIN
-- PERFORM THE DELETES ON FAMILY RELATIVE
DELETE dbo.FamilyRelative
FROM dbo.FamilyRelative fr
INNER JOIN deleted d
ON d.ID IN (fr.PersonID, fr.FamiliarID);
-- PERFORM THE DELETES ON PEOPLE
DELETE dbo.People
WHERE ID IN (SELECT d.ID FROM deleted d);
END
GO
Example on SQL Fiddle
I don't know how you would handle ON UPDATE CASCADE since when you update the primary key you lose the link between the inserted and deleted tables within your trigger.

Delete rows with foreign key in PostgreSQL

I would like to delete rows which contain a foreign key, but when I try something like this:
DELETE FROM osoby WHERE id_osoby='1'
I get this statement:
ERROR: update or delete on table "osoby" violates foreign key constraint "kontakty_ibfk_1" on table "kontakty"
DETAIL: Key (id_osoby)=(1) is still referenced from table "kontakty".
How can I delete these rows?
To automate this, you could define the foreign key constraint with ON DELETE CASCADE.
I quote the the manual for foreign key constraints:
CASCADE specifies that when a referenced row is deleted, row(s)
referencing it should be automatically deleted as well.
Look up the current FK definition like this:
SELECT pg_get_constraintdef(oid) AS constraint_def
FROM pg_constraint
WHERE conrelid = 'public.kontakty'::regclass -- assuming public schema
AND conname = 'kontakty_ibfk_1';
Then add or modify the ON DELETE ... part to ON DELETE CASCADE (preserving everything else as is) in a statement like:
ALTER TABLE kontakty
DROP CONSTRAINT kontakty_ibfk_1
, ADD CONSTRAINT kontakty_ibfk_1
FOREIGN KEY (id_osoby) REFERENCES osoby (id_osoby) ON DELETE CASCADE;
There is no ALTER CONSTRAINT command. Drop and recreate the constraint in a single ALTER TABLE statement to avoid possible race conditions with concurrent write access.
You need the privileges to do so, obviously. The operation takes an ACCESS EXCLUSIVE lock on table kontakty and a SHARE ROW EXCLUSIVE lock on table osoby.
If you can't ALTER the table, then deleting by hand (once) or by trigger BEFORE DELETE (every time) are the remaining options.
One should not recommend this as a general solution, but for one-off deletion of rows in a database that is not in production or in active use, you may be able to temporarily disable triggers on the tables in question.
In my case, I'm in development mode and have a couple of tables that reference one another via foreign keys. Thus, deleting their contents isn't quite as simple as removing all of the rows from one table before the other. So, for me, it worked fine to delete their contents as follows:
ALTER TABLE table1 DISABLE TRIGGER ALL;
ALTER TABLE table2 DISABLE TRIGGER ALL;
DELETE FROM table1;
DELETE FROM table2;
ALTER TABLE table1 ENABLE TRIGGER ALL;
ALTER TABLE table2 ENABLE TRIGGER ALL;
You should be able to add WHERE clauses as desired, of course with care to avoid undermining the integrity of the database.
There's some good, related discussion at http://www.openscope.net/2012/08/23/subverting-foreign-key-constraints-in-postgres-or-mysql/
You can't delete a foreign key if it still references another table.
First delete the reference
delete from kontakty
where id_osoby = 1;
DELETE FROM osoby
WHERE id_osoby = 1;
It's been a while since this question was asked, hope can help.
Because you can not change or alter the db structure, you can do this. according the postgresql docs.
TRUNCATE -- empty a table or set of tables.
TRUNCATE [ TABLE ] [ ONLY ] name [ * ] [, ... ]
[ RESTART IDENTITY | CONTINUE IDENTITY ] [ CASCADE | RESTRICT ]
Description
TRUNCATE quickly removes all rows from a set of tables. It has the same effect as an unqualified DELETE on each table, but since it does not actually scan the tables it is faster. Furthermore, it reclaims disk space immediately, rather than requiring a subsequent VACUUM operation. This is most useful on large tables.
Truncate the table othertable, and cascade to any tables that reference othertable via foreign-key constraints:
TRUNCATE othertable CASCADE;
The same, and also reset any associated sequence generators:
TRUNCATE bigtable, fattable RESTART IDENTITY;
Truncate and reset any associated sequence generators:
TRUNCATE revinfo RESTART IDENTITY CASCADE ;
It means that in table kontakty you have a row referencing the row in osoby you want to delete. You have do delete that row first or set a cascade delete on the relation between tables.
Powodzenia!
One can achieve this by issueing an extra SQL script that deletes the records related via the FK.
For this, using subselect in WHERE clause of a DELETE command can simply do what is needed.
Something similar to:
DELETE FROM kontakty
WHERE fk_column_from_kontakty_matching_id_osoby IN (
SELECT id_osoby FROM osoby WHERE id_osoby = '1'
);
DELETE FROM osoby WHERE id_osoby = '1';
the example assumes the FK column from kontakty matching osoby.id_osoby is called fk_column_from_kontakty_matching_id_osoby as it cannot be interpolated from the error message provided
For the case of OP's the query could by simplified a lot, but I have decided to leave it like this for it demonstrates accomplishing more complex scenarios
This approach is quite useful for SQL migrations, where one cannot depend on an assigned id and where ids cannot be easily fetched first and acted upon later.

Is there a generic way to replace referential constraints with triggers?

Porting application from MSSQL+Oracle to Sybase, and there's an issue with 'on delete cascade' - Sybase doesn't have the option.
Sybase has a link with a trigger to implement cascading delete: http://infocenter.sybase.com/help/index.jsp?topic=/com.sybase.help.ase_15.0.sqlug/html/sqlug/sqlug815.htm
but there is a problem with that solution when put into context of using it as 'on delete cascade'.
The problem, is triggers get executed after any referential constraints are checked.
The issue is illustrated here:
--drop table A
--drop table B
create table A (c int primary key)
create table B (c int primary key)
alter table A
add constraint Ac FOREIGN KEY (c) REFERENCES B(c)
create trigger delBA on B for delete
as delete A from A, deleted where A.c = deleted.c
insert into B values (1)
insert into A values (1)
delete B where c = 1
The 'delete' statement will fail, because of 'Ac' constraint. Had the trigger fired before the check for referential constraints (instead of after), it would have removed the value '1' from table 'A' and there would not be a problem.
For this reason, I'm thinking to implement the referential constraint by using a trigger. So I have to create an Insert and Update trigger, I believe. Is there some template that I can use?
I want to make sure I'm not overlooking anything, at first look at the issue, I missed that update trigger should be written so that it can validate that after update the constraint is still valid. - That is the reason I'm looking for a template, so I won't miss anything similar.
Triggers are often sources of trouble. A common approach would be to set up your data access layer that will create a transaction, delete the 'children' (cascade part) then delete the parent.

SQL Server: Self-reference FK, trigger instead of ON DELETE CASCADE

I need to perform an ON DELETE CASCADE on my table named CATEGORY, which has the following columls
CAT_ID (BIGINT)
NAME (VARCHAR)
PARENT_CAT_ID (BIGINT)
PARENT_CAT_ID is a FK on CAT_ID. Obviously, the lovely SQL Server does not let me use ON DELETE CASCADE claiming circular or multiple paths to deletion.
A solution that I see often proposed is triggers. I made the following trigger:
USE [ma]
GO
/****** Object: Trigger [dbo].[TRG_DELETE_CHILD_CATEGORIES] Script Date: 11/23/2009 16:47:59 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER TRIGGER [dbo].[TRG_DELETE_CHILD_CATEGORIES] ON [dbo].[CATEGORY] FOR DELETE AS
SET NOCOUNT ON
/* * CASCADE DELETES TO '[Tbl B]' */
DELETE CATEGORY FROM deleted, CATEGORY WHERE deleted.CAT_ID = CATEGORY.PARENT_CAT_ID
When I manually delete a category with child categories, I get the following exception:
Any idea what is wrong with my trigger?
UPDATE:
Sorry for the edit, but I have another column CATEGORY.CAT_SCH_ID, which is a FK of another table CAT_SCH.ID. This FK has a CASCADE DELETE as well, meaning that once I delete a CAT_SCH, its CATEGORies must also be deleted. So, I get this error when I define the trigger:
Cannot create INSTEAD OF DELETE or INSTEAD OF UPDATE TRIGGER 'TRG_DEL_CATEGORY_WITH_CHILDREN' on table 'CATEGORY'. This is because the table has a FOREIGN KEY with cascading DELETE or UPDATE.
Any ideas?
The FOR DELETE trigger is raised after the original DELETE has been executed. To delete recursively, you need to write an INSTEAD OF DELETE trigger.
The algorithm is like this:
Insert the PKs from deleted into a temp table
Find detail records of records in temp table
Loop until no more records are found
DELETE records in the table by joining with temp table.
I described recursive deletion in my blog.
Update
I guess you just need to drop that ON DELETE CASCADE flag from your recursive foreign key in Categories. The CASCADE flag on the foreign key from CAT_SCH should not matter.