2016-02-11 308 views
0

我有一个SQL触发器的问题。这是一个“AFTER INSERT”触发器。它适用于每个IF EXISTS块,除了具有更新和引发错误组合的块,如下所示。它要么更新并继续前进,要么停止更新。SQL触发器 - 插入后 - Update语句和RAISERROR不工作

代码:(我已关闭 - 在它的多个不同的尝试,他们都失败了)

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; 

的目标是表更新为1时,如果存在则是抓住并触发状态RAISERROR。 RAISERROR被java代码拾取并停止处理。如果我更新了该触发器,则会引发错误并停止。如果我把加注错误拿出来,触发器会更新但会继续 - 我不想要那个...我想要我的蛋糕并且也吃它!

想法?

+0

触发器启动一个隐式事务,所以你在触发器中的显式事务实际上是一个嵌套事务。 SQL Server不尊重嵌套事务,并且可能在那里发生了一些奇怪的事情。我只是删除你的明确交易,看看它是否适合你。 –

+2

更正以上评论。这并不是说SQL Server“不尊重嵌套事务”,但语义可能与你期望的有所不同。 SQL Server跟踪'@@ trancount'中的嵌套级别,并且只有外部'commit'(导致'@@ trancount'从1变为0的那个)将实际提交事务。然而,回滚(没有保存点)会回滚一个以“@@ trancount”为0的事务。 –

+2

BEGIN TRAN是多余的。它可以安全地删除而不影响触发器的行为,因为@JohnSpecko指出已经有一个隐式事务。然而,我不指望这能解决你遇到的任何问题。 [DC]。[dbo]。[Slips]是否有更新触发器,并且该更新触发器中是否有任何可能的ROLLBACK语句?可能发生的情况是,ROLLBACK正在回滚所有嵌套事务,包括你的工作。 –

回答

2

为了防止更新底层触发表,同时允许更新到滑动表,您需要一个INSTEAD OF INSERT触发器,一个VIEW和一个所有权链,以防止用户或其应用程序直接修改基础表码。由于之前在注释中提到的原因,您无法使用传统的AFTER UPDATE触发器,因为触发插入和触发的更新都包装在同一个事务中,它们必须同时生效或不生效。您不能仅提交事务的一部分,并且嵌套事务是它所包含的最外层事务的一部分。

现在的解决方案...

第一部分 - 查看 创建具有相同的列结构和命名为具有插入触发器是给你这一切麻烦的表的视图。形式的东西:

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的视图。用户和应用程序开发人员应将此视图视为他们正在使用的“表格”。

第2部分 - 所有权链 当引用对象和引用对象具有相同所有者时,在SQL Server中建立所有权链。当不同的一方(不是所有者)访问引用对象(在这种情况下,视图),用于该方的权限对引用对象像往常一样评价,但是未评价与被参考的对象(在这种情况下,表)。自从SQL Server开始以来,这一直是SQL Server的一项功能,但它并未被众多SQL开发人员所了解或很好理解。你可以得到更多关于ownership chains here的信息。

您将要拒绝权限的用户基础表,并将其授予视图。这意味着用户只能通过视图插入或更新行,而不能直接插入表中。因为你不希望他们绕过你将在接下来的部分怎么办...

3部分做到这一点是重要的 - INSTEAD OF触发器 对视图创建INSTEAD OF触发。其语法与AFTER触发器的语法类似,只不过INSTEAD OF出现在AFTER的位置,并且在被触发的位置没有发生任何插入操作,除非触发器本身对“接收器”表进行更新,否则不会更新将完全执行。这个触发器可以混合和匹配任何它想要的。与AFTER触发器一样,存在隐式事务,但仅执行触发器本身的显式数据修改操作。

请记住,触发器必须明确执行insert到底层汇表中。可以从inserted特殊表中检索要插入的行,就像AFTER触发器的情况一样。请记住,有可能(至少在SQL Server中)至少要插入一行(实际上在零行插入语句中可以有零行)。您需要决定是否允许好行或拒绝插入所有行。鉴于你的要求,我怀疑后者。

声音数据库设计的问题,我会强烈反对 -

  1. 触发限制插入到单排和
  2. 使用任何形式的游标的触发器内处理多行插入。改用面向集合的DML。

即使当前应用程序可能一次只能插入一行,但数据库不应该强加这样的限制。

合理的RAISERROR(这是一个合理的严重性和状态值)不会导致任何事情被中止或回滚。

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