SQL触发器无声地失败

时间:2014-11-05 10:12:32

标签: sql-server tsql triggers

我有以下触发器

CREATE TRIGGER [dbo].[LastActionTrigger] on [dbo].[PlanningAction]
AFTER INSERT AS

BEGIN

DECLARE @ActionID BIGINT
DECLARE @ActionNameID BIGINT
DECLARE @MilestonePlanningID BIGINT
DECLARE @Name VARCHAR(MAX)
DECLARE @log as varchar(max)
DECLARE @cmdtxt as varchar(255)

SELECT @ActionID = i.ActionID, @ActionNameID = i.ActionNameID, @MilestonePlanningID = i.MilestonePlanningID
FROM Inserted i


IF @ActionNameID NOT IN (10,11,12)
    BEGIN

    UPDATE MilestonePlanning SET LastPlanningActionID = @ActionID WHERE MilestonePlanningID = @MilestonePlanningID;

    --LOG
    SELECT @Name = AN.ActionName FROM ActionName AN WHERE AN.ActionNameID = @ActionNameID
    SET @log = 'Milestone['+CAST(@MilestonePlanningID AS VARCHAR(MAX))+'] : Last Action Changed To ' + @Name    
    EXEC master..xp_cmdshell @cmdtxt, no_output

    SELECT @cmdtxt = 'echo ' + @log + ' >> c:\database_logs\'+DB_NAME(DB_ID())+'_'+CAST(@MilestonePlanningID AS VARCHAR(MAX))+'.txt'
    SET @log = 'UPDATE MilestonePlanning SET LastPlanningActionID = '+CAST(@ActionID AS VARCHAR(MAX))+' WHERE MilestonePlanningID = ' + CAST(ISNULL(@MilestonePlanningID,0) AS VARCHAR(MAX))
    EXEC master..xp_cmdshell @cmdtxt, no_output

    SELECT @cmdtxt = 'echo ' + @log + ' >> c:\database_logs\'+DB_NAME(DB_ID())+'_'+CAST(@MilestonePlanningID AS VARCHAR(MAX))+'.txt'
    EXEC master..xp_cmdshell @cmdtxt, no_output
    EXEC(@log)
    END  

END

我们的系统使用播放暂停芬兰语和其他操作,其中所有操作都与此触发器一起使用,但有时Update语句无声地失败或永远不会发生,但是日志文件被创建(出于测试目的,我写了日志文件,看看发生了什么)

编辑: 我尝试了以下,但仍然无法正常工作,MilestonePlanning行得到更新,有些不会

ALTER TRIGGER [dbo].[LastActionTrigger] on [dbo].[PlanningAction]
AFTER INSERT AS

BEGIN


UPDATE MilestonePlanning SET LastPlanningActionID = i.ActionID
FROM Inserted i
WHERE MilestonePlanning.MilestonePlanningID = i.MilestonePlanningID AND i.ActionNameID NOT IN (10,11,12)

END
编辑:上面的Trigger Worked,主要问题是在代码后面某个动作更新了MilestonePlanning触发器触发后,这导致前一个LastPlanningActionID被重置

1 个答案:

答案 0 :(得分:2)

您的根本缺陷是,您似乎希望触发器每行一次 - 这在SQL Server中是 NOT 。相反,触发器会在每个语句时触发,并且伪表InsertedDeleted可能(并且将!)包含多行

鉴于该表可能包含多行 - 您希望在这里选择哪一行?

SELECT @ActionID = i.ActionID, @ActionNameID = i.ActionNameID, @MilestonePlanningID = i.MilestonePlanningID
FROM Inserted i

未定义 - 您可以从Inserted中的任意行获取值 - 而忽略所有其他行.....

您需要使用Inserted WILL 包含多行的知识重写整个触发器!您需要使用基于集合的操作 - 不要只期望Inserted中的一行!

另外:我强烈建议在触发器中进行任何耗时的处理 - 绝对不要调用外部依赖项,例如xp_cmdshell。触发器在正在运行的事务的上下文中执行,任何冗长的处理都将延长该事务的执行时间。触发器应该非常灵活并且不需要进行大量处理!