我遇到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代码拾取并停止发生处理。如果我取消更新,触发器会引发错误并停止。如果我把加速错误拿出来,触发器会更新但继续前进 - 我不希望这样......我想要我的蛋糕并且也吃它!
思想?
答案 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)要插入多行(事实上,在零行插入语句的情况下可以有零行)。您需要决定是否允许所有行的良好行或拒绝插入。鉴于你的要求,我怀疑后者。
作为健全的数据库设计问题,我强烈建议不要 -
即使当前应用程序一次只能插入一行,数据库也不应该施加这样的限制。
合理的RAISERROR
(具有合理的严重性和状态值的那个)不会导致任何内容被中止或回滚。
在这个组合中做这些事情应该产生你想要的结果。