简体   繁体   English

T-SQL 触发器 - 如果满足条件,则删除更新的记录

[英]T-SQL trigger - delete updated record if condition is met

I need to write a trigger for all tables in a Microsoft SQL Server 2017 Enterprise database that if column is_deleted = 1 and change_user = 'purge' , then delete the updated row instead of updating it.我需要为 Microsoft SQL Server 2017 Enterprise 数据库中的所有表编写一个触发器,如果列is_deleted = 1change_user = 'purge' ,则删除更新的行而不是更新它。

This needs to be a dynamic trigger so that each table trigger does not need to have the table explicitly defined.这需要是一个动态触发器,以便每个表触发器不需要显式定义表。 The environment has over 1200 tables.该环境有超过 1200 个表。

However, I only want the delete statement to delete the inserted record and not go through the entire table which I believe the below trigger would do.但是,我只希望删除语句删除插入的记录,而不是 go 整个表,我相信下面的触发器会这样做。 The environment has tables with 100+ million records and a delete statement that would run through the whole table would create a lot of overhead and would likely not keep up with 2 million transactions per hour.该环境具有包含 100+ 百万条记录的表,并且将贯穿整个表的删除语句会产生大量开销,并且可能无法跟上每小时 200 万个事务。

CREATE OR ALTER TRIGGER [PurgeDelete]
ON [TriggerTest]
AFTER UPDATE
AS
    DECLARE @SQL NVARCHAR(1000)
    DECLARE @SchemaName NVARCHAR(100) = (SELECT OBJECT_SCHEMA_NAME( parent_id ) FROM sys.triggers WHERE object_id = @@PROCID)
    DECLARE @TableName NVARCHAR(100) = (SELECT OBJECT_NAME( parent_id ) tableName FROM sys.triggers WHERE object_id = @@PROCID)

    IF (ROWCOUNT_BIG() = 0)
        RETURN;

    IF EXISTS (SELECT 1
               FROM Inserted AS i
               WHERE is_deleted = 1
               AND change_user = 'purge')
    BEGIN
        SET @SQL = 'DELETE FROM ' + @SchemaName + '.' + @TableName + ' WHERE is_deleted = 1 AND change_user = ''purge'''
        EXECUTE sp_executesql @SQL
    END
GO

UPDATE [TriggerTest] 
SET [Description] = 'Test 1a', [is_deleted] = 1, [change_user] = 'Purge' 
WHERE [TriggerTestId] = 1

SELECT * FROM [TriggerTest];
GO

Your trigger code needs to access the inserted and deleted tables.您的触发器代码需要访问inserteddeleted的表。 You cannot do this with dynamic SQL, as you are then in a different scope.您无法使用动态 SQL 执行此操作,因为您在不同的 scope 中。

In any case, if you want a dynamic trigger, I would advise instead to make a script to generate the triggers dynamically, but the triggers would be static once generated.在任何情况下,如果您想要一个动态触发器,我会建议改为制作一个脚本来动态生成触发器,但一旦生成触发器将是 static。

So the basic template would be like this:所以基本模板是这样的:

CREATE OR ALTER TRIGGER TriggerTest_PurgeDelete
ON dbo.TriggerTest
AFTER UPDATE
AS

    SET NOCOUNT ON;

    IF EXISTS (SELECT 1
               FROM inserted AS i
               WHERE is_deleted = 1
               AND change_user = 'purge')
    BEGIN
        DELETE FROM t
        FROM dbo.TriggerTest t
        JOIN inserted i ON i.YourPrimaryKey = t.YourPrimaryKey
        WHERE i.is_deleted = 1
          AND i.change_user = 'purge';
    END;

Now you just need a script that can generate this dynamically现在你只需要一个可以动态生成的脚本

-- user provided table/schema name here
DECLARE @schema sysname = 'dbo';
DECLARE @table sysname = 'TriggerTest';


DECLARE @quoted nvarchar(1000) = QUOTENAME(@schema) + '.' + QUOTENAME(@table);

DECLARE @primaryKeys nvarchar(max);

SELECT @primaryKeys = STRING_AGG(
  'i.' + QUOTENAME(c.name) + ' = t.' + QUOTENAME(c.name),
  ' AND '
  )
FROM sys.tables t
JOIN sys.schemas s ON s.schema_id = t.schema_id
JOIN sys.indexes i ON t.object_id = i.object_id
    AND i.is_primary_key = 1
JOIN sys.index_columns ic ON ic.object_id = i.object_id
    AND ic.index_id = i.index_id
JOIN sys.columns c ON c.object_id = t.object_id
    AND c.column_id = i.column_id
WHERE s.name = @schema
  AND t.name = @table
  AND pk.type = 'P';


DECLARE @sql nvarchar(max) = '
CREATE OR ALTER TRIGGER ' + QUOTENAME(@schema + '_' + @table + '_PD') + '
ON ' + @quoted + '
AFTER UPDATE
AS

    SET NOCOUNT ON;

    IF EXISTS (SELECT 1
               FROM inserted AS i
               WHERE is_deleted = 1
               AND change_user = 'purge')
    BEGIN
        DELETE FROM t
        FROM ' + @quoted + ' t
        JOIN inserted i ON ' + @primaryKeys + '
        WHERE i.is_deleted = 1
          AND i.change_user = ''purge'';
    END;
';

EXEC sp_executesql @sql;

You can in theory use an INSTEAD OF trigger, which may possibly be more efficient if the delete will happen often, but it's also more difficult to manage, as it needs updating every time you ALTER any of the columns in the table.理论上,您可以使用INSTEAD OF触发器,如果删除经常发生,这可能会更有效,但它也更难以管理,因为每次ALTER表中的任何列都需要更新。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM