SQL触发器 - 插入后 - 更新语句和RAISERROR无法正常工作

时间:2016-02-11 21:35:02

标签: sql-server tsql triggers sql-insert

我遇到SQL触发器问题。这是一个“AFTER INSERT”触发器。它适用于每个IF EXISTS块,除了具有更新和引发错误组合的块,如下所示。它要么更新,要么继续或停止,不要更新。

代码:(我离开的地方 - 多次尝试不同而且都失败了)

   IF EXISTS ( SELECT
                    [RPS].[Slip]
               FROM
                     [DC].[dbo].[Slips] AS [RPS]
               WHERE
                     [RPS].[Slip] = @ps
                     AND [RPS].[Status] = 0 )
               BEGIN    
                    BEGIN TRAN;
                          UPDATE
                                [DC].[dbo].[Slips]
                          SET
                                [Slip].[Status] = 1
                          FROM
                                [DC].[dbo].[Slips]
                          WHERE
                                [Slips].[Slip] = @ps;

                         SET @msg = ' ' + @NewLine + 'Inv. Decremented - Rollback' + @NewLine + 'Contact HD.' + @NewLine;

                         RAISERROR (@msg,16,1); 
                         COMMIT TRAN;
                         RETURN;
                END;

目标是在捕获IF EXISTS时将表更新为状态1并触发RAISERROR。 RAISERROR由java代码拾取并停止发生处理。如果我取消更新,触发器会引发错误并停止。如果我把加速错误拿出来,触发器会更新但继续前进 - 我不希望这样......我想要我的蛋糕并且也吃它!

思想?

1 个答案:

答案 0 :(得分:2)

为了防止更新到基础触发表,同时允许更新Slips表,您需要INSTEAD OF INSERT触发器,VIEW和所有权链,以防止直接修改用户或其应用程序代码的基础表。由于前面在评论中提到的原因,您无法使用传统的AFTER UPDATE触发器到达那里,因为触发插入和触发更新都包含在同一事务中,它们必须生效或既不生效。您不能只提交事务的一部分,而嵌套事务是它所包含的最外部事务的一部分。

现在解决方案......

第1部分 - 视图 创建一个具有相同列结构的视图,并命名为具有插入触发器的表,这会给您带来所有这些麻烦。形式的东西:

CREATE VIEW [same-name-as-the-table-you-are-using]
AS
    SELECT <list-all-columns-explicitly-please-dont-use-star>
    FROM <original-table-with-slightly-different-name-now>

如果原始表名为XXX,则将其重命名为XXX_SINK,并使用XXX作为视图。用户和应用程序开发人员应该考虑这个视图&#34; table&#34;他们正在使用。

第2部分 - 所有权链 当引用对象和引用的对象具有相同的所有者时,在SQL Server中建立所有权链。当另一方(不是所有者)访问引用对象(在本例中为视图)时,该方的权限将像往常一样针对引用对象进行评估,但 未评估 针对引用的对象(在本例中为表)。这从一开始就是SQL Server的一个特性,但是许多SQL开发人员都不知道或不了解它。您可以获得有关ownership chains here的更多信息。

您将要拒绝对表的用户群的权限并将其授予视图。这意味着用户只能通过视图插入或更新行,而不能直接插入表中。这很重要,因为你不希望它们绕过你将在下一部分做的事情......

第3部分 - INSTEAD OF触发器 在视图上创建INSTEAD OF触发器。语法与AFTER触发器的语法类似,除了INSTEAD OF代替AFTER,并且在触发它时尚未发生插入操作,除非触发器本身对&#34; sink&#34;进行更新。表,根本不会进行更新。这个触发器可以混合和匹配任何它想要的。与AFTER触发器一样,存在隐含事务,但仅执行触发器本身中的显式数据修改操作。

请记住,触发器必须在基础接收器表中显式执行insert。可以从inserted特殊表中检索要插入的行,就像AFTER触发器的情况一样。请记住,至少可以(至少涉及SQL Server)要插入多行(事实上,在零行插入语句的情况下可以有零行)。您需要决定是否允许所有行的良好行或拒绝插入。鉴于你的要求,我怀疑后者。

作为健全的数据库设计问题,我强烈建议不要 -

  1. 触发器限制插入单行,
  2. 在触发器内使用任何形式的光标来处理多行插入。改为使用面向集合的DML。
  3. 即使当前应用程序一次只能插入一行,数据库也不应该施加这样的限制。

    合理的RAISERROR(具有合理的严重性和状态值的那个)不会导致任何内容被中止或回滚。

    在这个组合中做这些事情应该产生你想要的结果。