Delete trigger to delete child records - sql

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.

Related

DELETE statement conflicted in Database First

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

Unable to delete a row from a table due to a dependency/reference in another table

I have a stored procedure that deletes a record from a table where one of the columns matches a specified value:
ALTER PROCEDURE [dbo].[Sp_DelBranch] #datafield varchar(50)
AS
BEGIN
DELETE FROM Branch WHERE branchname = #datafield
END
Unfortunately I get the following error on execution:
The DELETE statement conflicted with the REFERENCE constraint "fk_BranchIdDept". The conflict occurred in database "MproWorkSpace", table "dbo.Department", column 'BranchId'.
Can anyone explain why I'm seeing this error?
conflicted with the REFERENCE constraint "fk_BranchIdDept".
This means, that the value you are trying to delete, is primary key in some other table and is referenced in this table as foreign key,i.e., a primary-foreign key relation is mapped through constraint fk_BranchIdDept ...So unless you delete the referred primary key, foreign key can not be removed from table.
Otherwise, this will lead to data inconsistency!
Look for Cascade Delete to help you in this!
SQL ON DELETE CASCADE, Which Way Does the Deletion Occur?
The DELETE statement conflicted with the REFERENCE constraint "fk_BranchIdDept".
The record you're trying to delete is referenced in other tables; deleting the Branch record would leave the other tables orphaned, and referencing something that no longer exists.
There are two approaches to this:
Delete all records in referenced tables before deleting from the Branch table
Perform a cascade delete (deleting from the Branch table triggers a delete in every referenced table)
The error means that there are some rows in Department table referencing the branch you're trying to delete.
You should either delete the corresponding rows from the Department table first, or change values in BranchId column for these rows, so they point to some other branch.

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.