Selectively delete items in a delete trigger

  • 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

  • Sorry, forgot to mention we are on SQL Server 2008 SP 3

  • 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.

  • 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.

  • 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.

  • 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

    We walk in the dark places no others will enter
    We stand on the bridge and no one may pass

Viewing 6 posts - 1 through 5 (of 5 total)

You must be logged in to reply to this topic. Login to reply