May 10, 2013 at 12:56 pm
I am rewriting a FOR DELETE trigger. The current trigger, written in 2004 when we were still on SQL Server 2000, assumes only one row will be deleted at a time; it checks tables on another server to see if the item has history in those tables. If it does, an error is raised and the record is not deleted. If not, then a row in a table on another server in another database is deleted and the record in the deleted table is processed.
This is the old trigger:
CREATE TRIGGER [dbo].[del_Test$Vendor] ON [dbo].[Test$Vendor]
FOR DELETE
AS
SET NOCOUNT ON
SET XACT_ABORT ON
DECLARE @TRUE BIT,
@FALSE BIT
SET @TRUE = 1
SET @FALSE = 0
DECLARE @cDbNameVARCHAR(50),
@cServerVARCHAR(50),
@cCommandNVARCHAR(1000),
@cStatementNVARCHAR(1500)
CREATE TABLE #cur_delete(can_delete bit)
SET @cDbName = DB_NAME()
SET @cServer = interface.dbo.cf_db_id(@cDbName)
SET @cStatement = ''
DECLARE @cVendorIdVARCHAR(20)
DECLARE cur_vendor CURSOR LOCAL FOR
SELECT [No_]
FROM deleted
OPEN cur_vendor
FETCH NEXT FROM cur_vendor
INTO @cVendorId
WHILE @@FETCH_STATUS = 0
BEGIN
SET @cStatement = 'Select SQL2000.dbo.cf_ven_delete(''''' + @cVendorId + ''''') as can_delete'
SET @cCommand = 'SELECT can_delete FROM OPENQUERY(' + @cServer + ', ''' + @cStatement + ''')'
Insert into #cur_delete
EXEC sp_executesql @cCommand
IF (SELECT can_delete FROM #cur_delete) = @TRUE
BEGIN
SET @cStatement =
'DELETE FROM ' + interface.dbo.cf_db_id(@cDbName)COLLATE Latin1_General_100_CS_AS + '.taxsystem.dbo.vendor
WHERE vendor_id = ''' + @cVendorId + ''''
Print @cStatement
EXEC sp_executesql @cStatement
END
ELSE
BEGIN
--- Raise an error, do not continue processing
RAISERROR ('Vendor Has History in In-House Systems. Cannot Delete.', 16, 1) WITH NOWAIT
END
FETCH NEXT FROM cur_vendor
INTO @cVendorId
END
CLOSE cur_vendor
DEALLOCATE cur_vendor
When multiple records are deleted, the old trigger fails.
I can't seem to wrap my mind around how to process the records so that the items in the deleted table will only be deleted if there is no history in the other tables. I know how to delete only the records in the related table, but not how to selectively process the records in the deleted table based on the presence/absence of history. Does anyone have any suggestions? The code I've written so far follows. The deleted records are coming from a third party application, so I have no control over the records coming in. Any help would be greatly appreciated!
ALTER TRIGGER [dbo].[del_Test$Vendor] ON [dbo].[Test$Vendor]
FOR DELETE
AS
SET NOCOUNT ON
SET XACT_ABORT ON
IF EXISTS (SELECT 'x'
FROM deleted d
JOIN LTQASQL1.shared.dbo.VendorHistory h
ON d.No_ = h.VendorId COLLATE SQL_Latin1_General_CP1_CI_AS
WHERE h.HasHistory = 1)
BEGIN
--This obviously will prevent all the records in the deleted table from being processed
--not just those that have history
RAISERROR ('Vendor Has History in In-House Systems. Cannot Delete.', 16, 1) WITH NOWAIT
RETURN
END
ELSE
BEGIN
DELETE tcv
FROM LTQASQL1.TaxSystem.dbo.Vendor tcv
JOIN deleted d
ON tcv.vendor_id = d.No_ COLLATE Latin1_General_100_CS_AS
JOIN LTQASQL1.shared.dbo.VendorHistory h
ON d.No_ = h.VendorId COLLATE SQL_Latin1_General_CP1_CI_AS
WHERE h.HasHistory = 0
END
May 10, 2013 at 12:59 pm
Sorry, forgot to mention we are on SQL Server 2008 SP 3
May 10, 2013 at 1:38 pm
AnnA-607068 (5/10/2013)
I am rewriting a FOR DELETE trigger. The current trigger, written in 2004 when we were still on SQL Server 2000, assumes only one row will be deleted at a time; it checks tables on another server to see if the item has history in those tables. If it does, an error is raised and the record is not deleted. If not, then a row in a table on another server in another database is deleted and the record in the deleted table is processed.This is the old trigger:
CREATE TRIGGER [dbo].[del_Test$Vendor] ON [dbo].[Test$Vendor]
FOR DELETE
AS
SET NOCOUNT ON
SET XACT_ABORT ON
DECLARE @TRUE BIT,
@FALSE BIT
SET @TRUE = 1
SET @FALSE = 0
DECLARE @cDbNameVARCHAR(50),
@cServerVARCHAR(50),
@cCommandNVARCHAR(1000),
@cStatementNVARCHAR(1500)
CREATE TABLE #cur_delete(can_delete bit)
SET @cDbName = DB_NAME()
SET @cServer = interface.dbo.cf_db_id(@cDbName)
SET @cStatement = ''
DECLARE @cVendorIdVARCHAR(20)
DECLARE cur_vendor CURSOR LOCAL FOR
SELECT [No_]
FROM deleted
OPEN cur_vendor
FETCH NEXT FROM cur_vendor
INTO @cVendorId
WHILE @@FETCH_STATUS = 0
BEGIN
SET @cStatement = 'Select SQL2000.dbo.cf_ven_delete(''''' + @cVendorId + ''''') as can_delete'
SET @cCommand = 'SELECT can_delete FROM OPENQUERY(' + @cServer + ', ''' + @cStatement + ''')'
Insert into #cur_delete
EXEC sp_executesql @cCommand
IF (SELECT can_delete FROM #cur_delete) = @TRUE
BEGIN
SET @cStatement =
'DELETE FROM ' + interface.dbo.cf_db_id(@cDbName)COLLATE Latin1_General_100_CS_AS + '.taxsystem.dbo.vendor
WHERE vendor_id = ''' + @cVendorId + ''''
Print @cStatement
EXEC sp_executesql @cStatement
END
ELSE
BEGIN
--- Raise an error, do not continue processing
RAISERROR ('Vendor Has History in In-House Systems. Cannot Delete.', 16, 1) WITH NOWAIT
END
FETCH NEXT FROM cur_vendor
INTO @cVendorId
END
CLOSE cur_vendor
DEALLOCATE cur_vendor
When multiple records are deleted, the old trigger fails.
I can't seem to wrap my mind around how to process the records so that the items in the deleted table will only be deleted if there is no history in the other tables. I know how to delete only the records in the related table, but not how to selectively process the records in the deleted table based on the presence/absence of history. Does anyone have any suggestions? The code I've written so far follows. The deleted records are coming from a third party application, so I have no control over the records coming in. Any help would be greatly appreciated!
ALTER TRIGGER [dbo].[del_Test$Vendor] ON [dbo].[Test$Vendor]
FOR DELETE
AS
SET NOCOUNT ON
SET XACT_ABORT ON
IF EXISTS (SELECT 'x'
FROM deleted d
JOIN LTQASQL1.shared.dbo.VendorHistory h
ON d.No_ = h.VendorId COLLATE SQL_Latin1_General_CP1_CI_AS
WHERE h.HasHistory = 1)
BEGIN
--This obviously will prevent all the records in the deleted table from being processed
--not just those that have history
RAISERROR ('Vendor Has History in In-House Systems. Cannot Delete.', 16, 1) WITH NOWAIT
RETURN
END
ELSE
BEGIN
DELETE tcv
FROM LTQASQL1.TaxSystem.dbo.Vendor tcv
JOIN deleted d
ON tcv.vendor_id = d.No_ COLLATE Latin1_General_100_CS_AS
JOIN LTQASQL1.shared.dbo.VendorHistory h
ON d.No_ = h.VendorId COLLATE SQL_Latin1_General_CP1_CI_AS
WHERE h.HasHistory = 0
END
You may want to look at INSTEAD OF triggers instead of AFTER triggers.
May 10, 2013 at 1:41 pm
If I used an instead of trigger, and deleted records on the table that has an instead of trigger from the instead of trigger, then the instead of trigger would be fired from inside itself. I think that might be a problem.
May 10, 2013 at 2:57 pm
AnnA-607068 (5/10/2013)
If I used an instead of trigger, and deleted records on the table that has an instead of trigger from the instead of trigger, then the instead of trigger would be fired from inside itself. I think that might be a problem.
Read about INSTEAD OF triggers. They are not recursive. If you do a DELETE inside of a DELETE INSTEAD OF trigger, the DELETE INSTEAD OF trigger does NOT fire again.
May 10, 2013 at 3:14 pm
Triggers (any triggers) aren't recursive. AFTER triggers can be made recursive by turning the database option on (though I haven't seen many cases where that's a good idea)
Gail Shaw
Microsoft Certified Master: SQL Server, MVP, M.Sc (Comp Sci)
SQL In The Wild: Discussions on DB performance with occasional diversions into recoverability
Viewing 6 posts - 1 through 5 (of 5 total)
You must be logged in to reply to this topic. Login to reply