Let me start out trying to explain… My MSSQL2008 R2 database has two tables, one called SalesHeader the other SalesDetails. If I delete a header record it cascades and deletes all details related to that header. I also have an on delete trigger on the SalesDetails table that renumbers the DetailNumber field for the remaining details.
The Cascade on delete worked before I put in the Trigger on the Details table. The trigger works now but I get an error when trying to delete a header record. I can manually disable the delete trigger on details table and the header delete works as expected.
To fix this I am not quite sure whether a new on delete trigger on the header table that would disable the trigger on the details table would work or if there is a way to detect the Cascade on Delete in the Details trigger so the trigger logic could be bypassed. I have read here on SO that INSTEAD OF DELETE Triggers are not allowed with Cascade on Delete.
Any suggestions would be greatly appreciated and code snippets even more so.
Cheers,
SQL Trigger:
USE [MyDatabase]
GO
/****** Object: Trigger [dbo].[OnDeleteTrigger] Script Date: 9/11/2014 11:51:40 AM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author: Randy Gordey
-- Create date: 08/28/2014
-- Description: Renumbers the remaining details
-- when rows are deleted.
-- =============================================
CREATE TRIGGER [dbo].[OnDeleteTrigger]
ON [dbo].[SalesDetail]
AFTER DELETE
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for trigger here
PRINT 'Delete Trigger Fired'
DECLARE #ID int
DECLARE #RowNum int
DECLARE #TransactionNumber int
-- Gets the TransactionNumber from the deleted row
SET #TransactionNumber = (SELECT
intTransactionNumber
FROM DELETED)
DECLARE myCursor CURSOR
/** Gets a collection of the detail lines for this Transaction **/
LOCAL SCROLL STATIC FOR SELECT
intDetailNumber,
ROW_NUMBER() OVER (ORDER BY intDetailNumber) AS 'RowNum'
FROM SalesDetail
WHERE intTransactionNumber = #TransactionNumber;
OPEN myCursor;
-- Grabs the first row
FETCH NEXT FROM myCursor INTO #ID, #RowNum;
-- Begin Loop
WHILE ##FETCH_STATUS = 0 BEGIN
/** Updates Database: Setting new Detail Number for each row. **/
UPDATE SalesDetail
-- intDetailNumber is part of the PrimaryKey but it is not
-- set as an identity, so we can still change it.
SET intDetailNumber = #RowNum
WHERE intDetailNumber = #ID
AND intTransactionNumber = #TransactionNumber;
-- Grabs the next row
FETCH NEXT FROM myCursor INTO #ID, #RowNum;
END;
CLOSE myCursor;
DEALLOCATE myCursor;
END
GO
I found my fix.
SET #Count = (SELECT
COUNT(*)
FROM DELETED)
-- We want to break for multiple rows (ON DELETE CASCADE)
IF (#Count > 1) RETURN
I only delete single rows from the ChildTable at a time. I also want to keep the cascade on delete - as my ChildTable has Children of its own.
Related
One of the external application is inserting more than 40K records in the SQL Azure table.
I have a trigger to process all of the required rows which matches the column value for the unique column value for the distinct record
Whenever the 40K+ record in inserted the trigger is not able to fetch or trigger for all of the records and just getting 1 and 2 records sometimes.
In trigger how can I get distinct column value and order by.
inserting into temptables insert fewer columns only and random
How can i do batch processing from the trigger for the bulk insert
/****** Object: Trigger [dbo].[PriceStagingInsertTrigger] Script Date:
29/09/2020 13:46:24 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER TRIGGER [dbo].[PriceStagingInsertTrigger] on [dbo].
[SalesPriceStaging]
AFTER INSERT
AS DECLARE #ItemNumber NVARCHAR(20),
#applicablefromdate datetime,
#partycodetype int
SELECT #ItemNumber = ins.ITEMNUMBER FROM INSERTED ins;
SELECT #applicablefromdate = ins.PRICEAPPLICABLEFROMDATE FROM INSERTED ins;
SELECT #partycodetype = ins.PARTYCODETYPE FROM INSERTED ins;
SELECT * into #temptables from inserted
EXEC dbo.spSalesPriceStaging #ItemNumber,#applicablefromdate,#partycodetype
PRINT 'Stored procedure spSalesPriceStaging executed and completed'
The (BAD) solution is :
ALTER TRIGGER [dbo].[PriceStagingInsertTrigger]
ON [dbo].[SalesPriceStaging]
AFTER INSERT
AS
SET NOCOUNT ON;
DECLARE #ItemNumber NVARCHAR(20),
#applicablefromdate datetime,
#partycodetype int;
DECLARE C CURSOR
FOR
SELECT ITEMNUMBER, PRICEAPPLICABLEFROMDATE, PARTYCODETYPE
FROM inserted;
OPEN C;
FETCH C INTO #ItemNumber, #applicablefromdate, #partycodetype;
WHILE ##fetch_status = 0
BEGIN
EXEC dbo.spSalesPriceStaging #ItemNumber,#applicablefromdate,#partycodetype;
FETCH C INTO #ItemNumber, #applicablefromdate, #partycodetype;
END;
CLOSE C;
DEALLOCATE C;
GO
As they say, trigger in SQL Server have a set based logic and fires only one time even if there is 3657435435143213213 rows that are inserted.
The presence of a variable in the code is generally a pattern a bad code design....
I can't execute a procedure in my trigger with a parameter from the inserted pseudo table.
ALTER TRIGGER [dbo].[Restauracja]
ON [dbo].[RezerwacjaPozycje]
AFTER INSERT, UPDATE, DELETE
AS
BEGIN
DECLARE #ID INT;
SELECT IdReservation = #ID
FROM INSERTED
EXEC SumOrderPositions #ID;
END
If I change #id to f.e. 9. it correctly updates what I want for IdReservation number 9.
If you are inserting only one row, you can simply do the assignment in the correct order:
BEGIN
DECLARE #ID int;
SELECT #ID = IdReservation
FROM INSERTED;
EXEC SumOrderPositions #ID;
END;
However, this is a bug waiting to happen. Instead, you may want to use a cursor, loop through the rows in inserted using some other mechanism, or check that there is only one row in inserted. I'm not sure the exactly logic that you want.
We just want to maintain table log (every changes) history in MS SQL by write trigger please suggest
i tried but not working
CREATE TRIGGER [dbo].[update_ServiceDescriptionTable]
ON ServiceDescriptionMaster
After UPDATE
AS
BEGIN
declare #Rate money;
Select #Rate = Rate from inserted;
update [dbo].[ServiceDescriptionMasterlog] set Rate = #Rate
where Service_Description = '';
END
Ya good.
If you want to maintain evry changes log then you can insert in same log table with all field like as follows:
1) create same table like "ServiceDescriptionMasterlog" with one Extra Field (Column) Entry_DateTime set default bind getdate() method.
2) write a trigger on "ServiceDescriptionMaster" table as follows:
ALTER TRIGGER [dbo].[ServiceDescriptionMaster_OnUpdate]
ON [dbo].[ServiceDescriptionMaster]
After UPDATE
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO [dbo].[ServiceDescriptionMasterLog]
(S_No,Rate,.....)
select S_No,Rate,.....
from Deleted;
END
you can also maintain on delete:
ALTER TRIGGER [dbo].[ServiceDescriptionMaster_OnDelete]
ON [dbo].[ServiceDescriptionMaster]
For Delete
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO [dbo].[ServiceDescriptionMasterLog]
(S_No,Rate,.....)
select S_No,Rate,.....
from Deleted;
END
Hi guys I wanna create trigger to compare a line from inserted table with a table before insertion i have 1 table called "cv_langues " with 2 columns "id_cv,id_langues" here is my trigger :
alter trigger insertion_cv_langue
on cv_langues
for insert
as
begin
declare #id_langue int,#id_cv int, #inserted_langue int,#inserted_cv int,#comp int
set #comp=0
declare cv_langues cursor
for select id_langue,id_cv from cv_langues
declare inserted_cv_langues cursor
for select id_langue,id_cv from inserted
open inserted_cv_langues
fetch inserted_cv_langue into #inserted_langue,#inserted_cv //1st line to compare
close inserted_cv_langue
open cv_langues
while ##fetch_status = 0
begin
fetch cv_langues_1 into #id_langue,#id_cv // multi lines from cv_langues
if #id_langue = #inserted_langue and #id_cv = #inserted_cv
begin
set #comp = #comp+1
end
end
if #comp =2
rollback
close cv_langues_1
end
deallocate inserted_cv_langue
deallocate cv_langues_1
Instead of a trigger, try a unique constraint:
ALTER TABLE cv_langues ADD CONSTRAINT u_IdLangues UNIQUE (id_cv, id_langues)
This will prevent any duplicate entries from being inserted for the same user and will be a hell of a lot easier and more efficient than a trigger.
I have the following trigger
ALTER TRIGGER [dbo].[RefreshProject]
ON [dbo].[Project]
AFTER INSERT,DELETE,UPDATE
AS
BEGIN
SET NOCOUNT ON
DECLARE #percentage decimal(18,2)
DECLARE #ProjectID int
DECLARE #TASKID int
IF EXISTS(SELECT * FROM Inserted)
BEGIN
SELECT #ProjectID = Inserted.ProjectID, #TASKID = ISNULL(Inserted.TASKID,-1) from Inserted join Project on Inserted.ScheduleID = Project.ScheduleID
END
IF EXISTS(SELECT * FROM Deleted)
BEGIN
SELECT #ProjectID = Deleted.ProjectID,#TASKID = ISNULL(Deleted.TASKID,-1) from Deleted join Project on Deleted.ScheduleID = Project.ScheduleID
END
BEGIN
SET #percentage = (SELECT percentage from Project where ProjectID = #ProjectID)
EXEC LoadSummary #percentage,#ProjectID, #TASKID
END
END
For Inserts and updates I am able to get the ProjectID of the modified object, however when an item is deleted, I can not get the ProjectID or TaskID... Any Idea what I'm doing wrong?
The problem is that your trigger is an AFTER trigger, which means that the row has already been deleted from the Project table by the time the trigger is fired. So, you can't join the deleted view to Project, because the corresponding row won't exist. You will need to assign the variables you want directly from the deleted view.
Also, as Mitch's note mentions above, you may want to use a cursor to iterate over all rows in the inserted/deleted views if multiple rows can be updated at a time. Alternatively, you could raise an error at the beginning of your trigger if ##ROWCOUNT is greater than one, to prevent multiple row updates from causing your trigger to misbehave.