T-SQL 更新触发器

Posted

技术标签:

【中文标题】T-SQL 更新触发器【英文标题】:T-SQL Update Trigger 【发布时间】:2018-11-08 17:52:21 【问题描述】:

我正在尝试在 SQL Server 中创建以下触发器,但 SSMS 引发错误,我不知道它是什么。有什么想法吗?

消息 156,第 15 级,状态 1,第 2 行 关键字“触发器”附近的语法不正确。

代码:

IF NOT EXISTS(SELECT * FROM sys.triggers 
              WHERE object_id = OBJECT_ID(N'[dbo].[trAfterUpdateInfoDoc]'))
    CREATE TRIGGER [dbo].[trAfterUpdateInfoDoc]
    ON [dbo].[InfoDocs]
    AFTER UPDATE
    AS
    BEGIN
        DECLARE @infodoctemplateid INT;
        DECLARE @infodocid INT;
        DECLARE @requireccount FLOAT(2);
        DECLARE @filledcount FLOAT(2);
        DECLARE @pcnt FLOAT(2);

        DECLARE c CURSOR FOR
             SELECT id 
             FROM InfoDocs ifd 
             WHERE exists (SELECT 1 FROM Inserted AS i WHERE i.id = ifd.id)

        OPEN c

        FETCH NEXT FROM c INTO @infodocid

        WHILE @@Fetch_Status = 0 
        BEGIN
            SELECT @infodoctemplateid = InfoDocTemplateId 
            FROM InfoDocs 
            WHERE id = @infodocid;

            SELECT @requireccount = COUNT(*) 
            FROM InfoDocTemplateFields 
            WHERE InfoDocTemplateId = @infodoctemplateid 
              AND IsRequired = 1;

            IF (@requireccount = 0)
            BEGIN
                set @pcnt = 100;
            END
            ELSE
            BEGIN
                select @filledcount = count(*) from InfoDocFields 
                where InfoDocId = @infodocid 
                and InfoDocTemplateFieldId in (select id from InfoDocTemplateFields where InfoDocTemplateId = @infodoctemplateid and IsRequired = 1)
                and (BooleanValue is not null or (StringValue is not null and StringValue <> '') or IntValue is not null or DateValue is not null)

                set @pcnt = @filledcount / @requireccount * 100.0;
            END
            update InfoDocs set PercentageCompleted = @pcnt Where id = @infodocid;

            Fetch next From c into @infodocid
        End
    Close c
    Deallocate c
END

【问题讨论】:

我不知道为什么会出现语法错误,但是游标只能作为最后的手段使用,因为它们是性能杀手,并且在触发器中几乎应该不惜一切代价避免它们。您应该重写您的触发器以使用基于集合的方法。 你能给我指出一个替代方法来循环一个集合而不使用游标 也许,但这是一个不同的问题 - 应该在不同的帖子上。我知道一开始这看起来很琐碎和烦人,但当你仔细想想,你提出的问题已经被 Martin Smith 回答了——所以你应该接受它并继续下一个问题。 根据要求创建一个新案例:***.com/questions/53222867/… 【参考方案1】:

Create Trigger(限制部分)必须是批处理中的第一个语句,因此不能在它之前使用 IF 存在检查。

在 SQL Server 2016 SP1 及更高版本中,您可以使用 CREATE OR ALTER TRIGGER... 来实现相同的行为。

Pre-SQL Server 2016 SP1,有一些建议here

我也赞同 Zohar 的评论,即将此逻辑放入触发器很可能会导致许多性能问题,并且可能难以追踪意外行为/错误。

【讨论】:

【参考方案2】:

无论何时创建触发器之类的 SQL 对象,它都必须是批处理中创建的唯一对象。批处理由关键字GO 终止。

尝试重构你的代码以适应这个一般结构,看看它是否有效:

IF OBJECT_ID(N'[dbo].[trAfterUpdateInfoDoc]') IS NOT NULL
    DROP TRIGGER [dbo].[trAfterUpdateInfoDoc]
GO

CREATE TRIGGER [dbo].[trAfterUpdateInfoDoc]
ON [dbo].[InfoDocs]
AFTER UPDATE
AS
BEGIN
    --PLACE CODE HERE
END
GO

【讨论】:

以上是关于T-SQL 更新触发器的主要内容,如果未能解决你的问题,请参考以下文章

t-sql如何创建连接2台服务器的触发器

触发器创建删除等操作

SQL Server 触发器切换插入、删除、更新

T-SQL触发器

T-SQL 触发器 - 审计列更改

列值更改的 T-SQL (SQL Server 2016) 触发器,在插入