Delete trigger not working when executed - sql-server

Is there any posibility to delete from table2 (which has a reference from table1) after delete one row in table1? (eg: if I delete a CustomerID from Customers at the same time to delete the order he made (OrderID))
CREATE TRIGGER DelBilete
ON Curse
AFTER DELETE
AS
BEGIN
DELETE FROM Bilete
WHERE IDCursa IN (SELECT IDCursa FROM Curse)
END

There are two ways to do this: either with a cascading delete (which you set up with your foreign key constraint):
ALTER TABLE [dbo].[Orders] WITH NOCHECK ADD CONSTRAINT
[FK_Orders_Customer] FOREIGN KEY([customerId])
REFERENCES [dbo].[Customer] ([customerId])
ON DELETE CASCADE
... or with a trigger, as you have tried. (You might try something like this, actually. Disclaimer: this was from memory and not tested.)
CREATE TRIGGER DelBilete
ON Curse
AFTER DELETE
AS
BEGIN
DELETE FROM Bilete WHERE IDCursa IN(SELECT IDCursa FROM deleted)
END
GO
If you just delete where the foreign key is present in the primary key table, you'll delete all your orders (or whatever Bilete contains)!
ETA: On getting a foreign key constraint error:
The delete statement conflicted with the reference constraint
"FK_Bilete_Curse".
If you have a foreign key constraint, and you want to delete from the primary table (Curse) and have the related records deleted from the related table (Bilete), an AFTER trigger won't solve the problem, precisely because it is an AFTER trigger. It takes place after the records have been deleted. If they can't be deleted, the trigger doesn't fire. And if those records are referenced by other records, they can't be deleted.
So you then have two options:
The cascading delete, above;
An INSTEAD OF trigger. (See here for more details.)
An INSTEAD OF trigger in your case would look like this (again, from memory and documentation, not tested):
CREATE TRIGGER DelBilete
ON Curse
INSTEAD OF DELETE
AS
BEGIN
DELETE FROM Bilete WHERE IDCursa IN(SELECT IDCursa FROM deleted)
DELETE FROM Curse WHERE IDCursa IN (SELECT IDCursa from deleted)
END
GO
With an INSTEAD OF trigger, when you execute code to delete a record from Curse, the trigger runs instead. It deletes the related records from Bilete, and then executes the delete from Curse.
INSTEAD OF triggers do not run recursively, so the DELETE from Curse in the trigger doesn't force another run of the trigger.

Related

Error on delete trigger

This trigger should delete a row from the parent table that is not deleted from the child table. The error is in the image below.
My code attempt:
CREATE TRIGGER ProductDeleted ON Product
for DELETE AS
BEGIN
DELETE FROM OrderItem
WHERE ProductID = (SELECT ProductID FROM DELETED)
END
help me please
You could simplify it by adding a CASCADE DELETE hint on a foreign key constraint such as
CREATE TABLE OrderItem
(
ID INT ,
ProductID NOT NULL UNIQUE
CONSTRAINT fk_Products
REFERENCES Products (ID) ON DELETE CASCADE
);
Since you already have a table, all you need to do is drop the constraint and recreate a new one.
ALTER TABLE OrderItem DROP
CONSTRAINT fk_ProductID;
ALTER TABLE OrderItem ADD
CONSTRAINT fk_ProductID
FOREIGN KEY (ID)
REFERENCES Product (ID)
ON DELETE CASCADE;
What this means, is that , any time you delete a record from the parent table (Product), child records from (OrderItem)will be deleted as well, so you dont have to use triggers, unless if you want to do some recording.
If you are really insisting on using triggers then you can tweak it a little bit like this :
ALTER TRIGGER ProductDeleted on Product
INSTEAD OF DELETE AS
BEGIN
SET NOCOUNT ON;
/* First we are deleting referenced columns in OrderItem table */
DELETE FROM OrderItem
where ProductID IN (select deleted.ID /* Columns from product Table */ from deleted)
/* Now we are doing actual delete statement */
DELETE FROM Products where ID IN (select deleted.ID from deleted)
END
But once again you should consider using ON CASCADE DELETE, its much simpler to setup, easier to maintain and you can have only one INSTEAD OF trigger per table, so if you ever need to do something more meaningful you would have to change this one, and add extra overhead.
Add SET NOCOUNT ON as the first thing in the trigger's body (after BEGIN).

How can I implement a SQL Server trigger as an alternative to ON DELETE CASCADE?

I want to use temporal table in SQL Server 2016. Currently temporal tables do not allow the use of ON DELETE CASCADE. How can I implement a trigger to imitate the behavior? I'd like to keep foreign keys because I use navigation properties of Entity Framework in my application.
I think an after trigger doesn't work because the delete statement doesn't work with foreign key condition.
Suppose we have the following simple situation:
UserRole.UserId: foreign key to User.Id
UserRole.RoleId: foreign key to Role.Id
If I delete the role with Id == 2 than I want to delete the second and fourth row in UserRole. How can I implement this without ON DELETE CASCADE in temporal tables?
Like I mentioned in the comments section you will need an Instead of Trigger, something like this......
CREATE TRIGGER tr_CascadeDelete_UserRole
ON [Role]
INSTEAD OF DELETE
AS
BEGIN
SET NOCOUNT ON;
--** Delete Rows from [UserRole] table first
DELETE FROM [UserRole]
WHERE EXISTS (SELECT 1
FROM Deleted d
WHERE d.RoleID = [UserRole].RoleID)
--** Delete Rows from [Role] table first
DELETE FROM [Role]
WHERE EXISTS (SELECT 1
FROM Deleted d
WHERE d.RoleID = [Role].RoleID)
END

Delete, Insert, Update using table from cache?

I have a stored procedure which runs a couple of deletes, inserts and updates for several tables.
My problem: The procedure causes key constraint violations.
Example:
DELETE FROM tableA WHERE key='1'
DELETE FROM tableB WHERE key='1'
Table B has a foreign key reference to table A, the procedure stops and I get an error message. All changes are discarded.
My question: How can I tell the procedure or server that the entry in table A is already deleted? Can I use table A from the cache for the foreign key reference comparisons?
I use a microsoft sql server.
EDIT
I'm sorry, I mixed the chronological order.
In fact, table A has the FK and its entry is the first one to be deleted.
Table B follows afterwards with no reference to table A.
Nevertheless, I get a "conflicted with the REFERENCE constraint" message.
The only explanation for this error message I have is, that the delete on table A is not committed.
EDIT
I may have found the problem. All my delete statements are enclosed in BEGIN/END.
So I have:
BEGIN
DELETE FROM tableA WHERE key='1'
DELETE FROM tableB WHERE key='1'
END
As far as I understand, such enclosed statements are run as one big statement before anything is committed.
Regards
You have two options:
To perform queries in the correct so the server will be able to execute them correctly.
You can get rid of foreign keys for those tables during the execution of the procedure.
If TABLEB has a FK from TABLEA then you must first delete fromTABLEB:
DELETE FROM tableB WHERE key='1' --Doing this will remove the row that references TABLEA
DELETE FROM tableA WHERE key='1' --TABLEA can be deleted because there are no references to the row in the other TABLE

Cannot truncate table because it is being referenced by a FOREIGN KEY constraint

I get the following message even when the table that references it is empty: "Cannot truncate table 'dbo.Link' because it is being referenced by a FOREIGN KEY constraint" Doesn't seem to make much sense why this is occurring. Any suggestions?
In SQL Server a table referenced by a FK cannot currently be truncated even if all referencing tables are empty or the foreign keys are disabled.
You need to use DELETE (may require much more logging) or drop the relationship(s) prior to using TRUNCATE and recreate them afterwards or see the workarounds on this connect item for a way of achieving this using ALTER TABLE ... SWITCH
You cannot truncate a table which has an FK constraint on it. As workaround, you could:
1/ Drop the constraints
2/ Trunc the table
3/ Recreate the constraints.
Here it is the associated T-SQL script, supposing you have 2 tables called MyTable and MyReferencedTable:
-- Remove constraint
IF EXISTS(SELECT 1 FROM sys.foreign_keys WHERE name = 'FK_MyReferencedTable_MyTable')
BEGIN
ALTER TABLE dbo.MyReferencedTable
DROP CONSTRAINT FK_MyReferencedTable_MyTable
END
-- Truncate table
TRUNCATE TABLE dbo.MyTable
-- Re-Add constraint
IF NOT EXISTS(SELECT 1 FROM sys.foreign_keys WHERE name = 'FK_MyReferencedTable_MyTable')
BEGIN
ALTER TABLE dbo.MyReferencedTable
WITH CHECK ADD CONSTRAINT [FK_MyReferencedTable_MyTable] FOREIGN KEY(ListingKey)
REFERENCES dbo.MyTable (ListingKey)
END
Execute the following query to search any constraint:
use MyDatabase
select c.name as c_name, t.name as t_name
from sys.key_constraints c
join sys.tables t on t.object_id = c.parent_object_id
If any constraint found on your table, remove it.
If you are receiving this error and you need to truncate the table then alternative solution could be that you can drop and re-create the table along with primary/other_keys/indexes/triggers. Please make sure that you don't need to the data in that table.
This soulution is working like a charm for me and hardly took a minute to finish. I am doing it for masking purpose.
Not for SQL Server but MySQL only.
Instead of deleting or recreating the constraint, I prefer this simpler way.
Disable the constraint validation by executing the following query first :
SET FOREIGN_KEY_CHECKS=0;
Then truncate your tables
And finally, reactivate the constraint validation :
SET FOREIGN_KEY_CHECKS=1;
Thats a common solution when you migrate databases, so you don't have to worry about the order the tables are inserted in.

INSTEAD OF DELETE Trigger conflict with ON DELETE CASCADE FK

Batches can have multiple Bills which can have multiple BillLines. I have ON DELETE CASCADE FKs between them so that if you delete a Batch, the associated Bill and BillLine records also get deleted. If you delete a Bill, the associated BillLines get deleted but the Batch record is not affected. Now I need to prevent the delete of a Bill if there is a certain data condition with one or more of the associated BillLine records.
Table Bill clearly needs an INSTEAD OF DELETE trigger. BillLine.BillId has an ON DELETE CASCADE FK referencing Bill.BillId. It makes sense that I need to make that FK ON DELETE NO ACTION instead, because the INSTEAD OF DELETE trigger effectively replaces the CASCADE functionality. When I delete a Bill, the INSTEAD OF DELETE will either delete the associated BillLine records or raise an exception depending on certain data conditions. So far, so good.
However, because Bill.BatchId has an ON DELETE CASCADE FK referencing Batch.BatchId, SQL Server will not let me create the trigger. This I do not understand. Why should I have to build an INSTEAD OF DELETE trigger on Batch just because I have one on Bill?
The code to create the tables and keys below (with all extraneous columns and keys omitted) is how things are now, with no ON DELETE CASCADE clauses. The question is, why can't FK_Bill_Batch_BatchId have that clause instead of my having to create an additional INSTEAD OF DELETE trigger?
CREATE TABLE [Batch](
[BatchId] [bigint] NOT NULL,
CONSTRAINT [PK_Batch_BatchId] PRIMARY KEY CLUSTERED
(
[BatchId] ASC
)
)
CREATE TABLE [Bill](
[BillId] [bigint] NOT NULL,
[BatchId] [bigint] NOT NULL,
[ReversesBillId] [bigint] NULL,
CONSTRAINT [PK_Bill_BillId] PRIMARY KEY CLUSTERED
(
[BillId] ASC
)
)
ALTER TABLE [Bill] WITH CHECK ADD CONSTRAINT [FK_Bill_Batch_BatchId] FOREIGN KEY([BatchId])
REFERENCES [Batch] ([BatchId])
ALTER TABLE [Bill] WITH NOCHECK ADD CONSTRAINT [FK_Bill_ReversesBillId] FOREIGN KEY([ReversesBillId])
REFERENCES [Bill] ([BillId])
CREATE TABLE [BillLine](
[BillLineId] [bigint] NOT NULL,
[BillId] [bigint] NOT NULL,
[ReversedByBillLineId] [bigint] NULL,
CONSTRAINT [PK_BillLine_BillLineId] PRIMARY KEY CLUSTERED
(
[BillLineId] ASC
)
)
ALTER TABLE [BillLine] WITH CHECK ADD CONSTRAINT [FK_BillLine_Bill_BillId] FOREIGN KEY([BillId])
REFERENCES [Bill] ([BillId])
ALTER TABLE [BillLine] WITH CHECK ADD CONSTRAINT [FK_BillLine_ReversedByBillLineId] FOREIGN KEY([ReversedByBillLineId])
REFERENCES [BillLine] ([BillLineId])
GO
CREATE TRIGGER [Bill_Delete]
ON [Bill]
INSTEAD OF DELETE
AS
BEGIN
SET NOCOUNT ON
DECLARE #BillId UNIQUEIDENTIFIER
DECLARE myCursor CURSOR LOCAL FORWARD_ONLY
FOR SELECT b.[BillId]
FROM deleted b
JOIN [Batch] bt on b.[BatchId] = bt.[BatchId]
OPEN myCursor
FETCH NEXT FROM myCursor INTO #BillId
WHILE ##FETCH_STATUS = 0
BEGIN
-- Delete BillLine records reversed by another BillLine in the same Bill
DELETE FROM [BillLine]
WHERE [BillId] = #BillId
AND [ReversedByBillLineId] IN
(SELECT bl.[BillLineId]
FROM [BillLine] bl
WHERE bl.BillId = #BillId
);
-- Delete all remaining BillLine records for the Bill
-- If the BillLine is reversed by a BillLine in a different Bill, the FK will raise an exception.
-- That is the desired behavior.
DELETE FROM [BillLine]
WHERE [BillId] = #BillId;
-- Delete the Bill
DELETE FROM [Bill]
WHERE [BillId] = #BillId;
FETCH NEXT FROM myCursor INTO #BillId
END
END
GO
CREATE TRIGGER [Batch_Delete]
ON [Batch]
INSTEAD OF DELETE
AS
BEGIN
SET NOCOUNT ON
DECLARE #BatchId UNIQUEIDENTIFIER
DECLARE myCursor CURSOR LOCAL FORWARD_ONLY
FOR SELECT [BatchId]
FROM deleted
OPEN myCursor
FETCH NEXT FROM myCursor INTO #BatchId
WHILE ##FETCH_STATUS = 0
BEGIN
-- Delete all Bill records for the Batch.
-- Another INSTEAD OF DELETE trigger on Bill will attempt to delete the associated BillLine records in the correct order.
-- If the BillLine is reversed by a BillLine in a different Bill, FK_BillLine_ReversedByBillLineId will raise an exception.
-- That is the desired behavior.
DELETE FROM [Bill]
WHERE [BatchId] = #BatchId;
FETCH NEXT FROM myCursor INTO #BatchId
END
END
If you try to replace the Batch_Delete trigger with ON DELETE CASCADE:
DROP TRIGGER [Batch_Delete]
ALTER TABLE [Bill] DROP CONSTRAINT [FK_Bill_Batch_BatchId];
ALTER TABLE [Bill] WITH CHECK ADD CONSTRAINT [FK_Bill_Batch_BatchId] FOREIGN KEY([BatchId])
REFERENCES [Batch] ([BatchId]) ON DELETE CASCADE;
You'll get this:
Msg 1787, Level 16, State 0, Line 2
Cannot define foreign key constraint 'FK_Bill_Batch_BatchId' with cascaded DELETE or UPDATE on table 'Bill' because the table has an INSTEAD OF DELETE or UPDATE TRIGGER defined on it.
Msg 1750, Level 16, State 0, Line 2
Could not create constraint. See previous errors.
I don't understand why an ON DELETE CASCADE in this direction should have anything to do with the INSTEAD OF DELETE trigger on the Bill table.
I know this is an old question, but it deserves an answer:
The reason why you cannot specify ON DELETE CASCADE when your child table has an INSTEAD OF DELETE trigger defined is because in your trigger you may decide to not delete the child table rows thus impeding the cascade to take effect.
Since there is no certainty that a cascade is possible the database doesn't know how to handle that situation and thus leaves the issue to the developer to solve.
In my opinion you do not need an INSTEAD OF trigger.
INSTEAD OF trigger work always instead of an operation.
You want to throw an exception in some cases - and delete in others.
So you could use DELETE CASCADE and a normal (AFTER) trigger.
Inside the trigger you can RAISERROR the exception and probably ROLLBACK your transaction.
(There is always an implicit transaction around a trigger)

Resources