SQL Server 2017:使用事务删除触发器

时间:2018-03-23 10:05:05

标签: sql-server triggers transactions

有2个表

CREATE TABLE dbo.[user] (
    user_id       INT           NOT NULL IDENTITY PRIMARY KEY,
    user_deleter INT REFERENCES [user] (user_id),
    user_deleted DATETIME2
);

CREATE TABLE dbo.[project] (
    project_id      INT           NOT NULL IDENTITY PRIMARY KEY,
    project_owner   INT           NOT NULL REFERENCES [user] (user_id)
);

对于dbo。[user]有一个触发器应该删除一个(或多个)用户(这就是while和DELETERSTABLE的用途)

CREATE OR ALTER TRIGGER delete_user
ON dbo.[user] 
INSTEAD OF DELETE
AS
BEGIN
    SELECT * INTO DELETERSTABLE 
    FROM DELETED
    WHILE (SELECT COUNT(*) FROM DELETERSTABLE) <> 0
    BEGIN
        DECLARE @id INT = (SELECT TOP 1 user_id FROM DELETERSTABLE)
        DECLARE @deleted DATETIME2 = SYSDATETIME()
        DECLARE @deleter INT = (SELECT TOP 1 dbo.[user].user_id FROM dbo.[user] INNER JOIN dbo.[usertype] ON dbo.[user].user_type = dbo.[usertype].type_id WHERE dbo.[usertype].type_name = 'ADMINISTRATOR')
        UPDATE dbo.[user] SET user_deleted = @deleted, user_deleter = @deleter WHERE user_id = @id
        DELETE FROM DELETERSTABLE WHERE user_id = @id
    END
    DROP TABLE DELETERSTABLE
END
GO

当我执行以下代码时:DELETE FROM dbo.[user]它运行良好。但我正在尝试向触发器添加事务(因此它不仅删除2个用户而不是继续删除其他3个用户),就像我在下面做的尝试一样

CREATE OR ALTER TRIGGER delete_user
ON dbo.[user] 
INSTEAD OF DELETE
AS
BEGIN
    BEGIN TRANSACTION
        BEGIN TRY
            SELECT * INTO DELETERSTABLE 
            FROM DELETED
            WHILE (SELECT COUNT(*) FROM DELETERSTABLE) <> 0
            BEGIN
                DECLARE @id INT = (SELECT TOP 1 user_id FROM DELETERSTABLE)
                DECLARE @deleted DATETIME2 = SYSDATETIME()
                DECLARE @deleter INT = (SELECT TOP 1 dbo.[user].user_id FROM dbo.[user] INNER JOIN dbo.[usertype] ON dbo.[user].user_type = dbo.[usertype].type_id WHERE dbo.[usertype].type_name = 'ADMINISTRATOR')
                UPDATE dbo.[user] SET user_deleted = @deleted, user_deleter = @deleter WHERE user_id = @id
                DELETE FROM DELETERSTABLE WHERE user_id = @id
            END
            DROP TABLE DELETERSTABLE
        END TRY
        BEGIN CATCH
            THROW 51001, 'CUSTOM ERROR: delete_user NOT executed', 1
            IF @@TRANCOUNT > 0
                ROLLBACK TRANSACTION
        END CATCH
    IF @@TRANCOUNT > 0
        COMMIT TRANSACTION
END
GO

当我执行以下代码时:DELETE FROM dbo.[user]我收到下一个错误

  

DELETE语句与REFERENCE约束“FK__project__project__36FC065D”冲突。冲突发生在数据库“***”,表“dbo.project”,列“project_owner”

如何在保持第一个触发器的结构的同时仍然使用事务

1 个答案:

答案 0 :(得分:0)

根本不需要WHILE循环或任何这些变量。整个触发器可以更改为:

CREATE OR ALTER TRIGGER delete_user
ON dbo.[user] 
INSTEAD OF DELETE
AS
BEGIN

    UPDATE dbo.[user]
    SET user_deleted = SYSDATETIME(),
        user_deleter = (SELECT TOP 1 dbo.[user].user_id
                        FROM dbo.[user]
                             INNER JOIN dbo.[usertype] ON dbo.[user].user_type = dbo.[usertype].type_id
                        WHERE dbo.[usertype].type_name = 'ADMINISTRATOR') --Why do you want the deleter to be a random user?
    WHERE user_id IN (SELECT user_id
                      from deleted);
END
GO

注意评论;你为什么TOP 1没有ORDER BY?您是否乐意将删除器标记为具有ADMINISTRATOR类型的随机用户? (这似乎不是预期的逻辑)。

在这个问题下引用我的评论:

  

你不能在触发器中声明一个事务,因为它已经在一个事件中;如果触发器的任何部分失败,则回滚整个事务。

因此,您无需申报交易。