2012-06-02 26 views
4

我知道我会为此发火,但是......无限触发循环....按设计(!)。如何解决?

我有表ProductA,ProductB和ProductC,它们具有非常相似的模式,但是每个表中有2或3列。每个表都有一个插入触发器,它将A,B或C中每个插入的重复行触发到表产品,这是所有产品的合并。另外,A,B或C上的更新触发器同样会更新Table Products中的等价行,删除触发器也是如此。所有的工作完美无瑕,直到.....我们更新,例如,表产品列A,它也存在于表A,B和C.

我正在寻找开发表的产品上的触发器,将传播在表A,B和C中的每一个表中,将列A中的列更新为BUT,而不调用表A,B和C上的更新触发器。期望的行为是更新在两个方向上工作而不产生无限循环(注意,在表产品只有2列需要回到复制到表A,B和C)

选项包括:

  1. 重新设计架构,以便这种情况并不存在(N在 卡中,这是一个快速解决方案,重新设计可以由别人完成 );
  2. 当我更新表产品 时,手动禁用触发器(这全部在应用程序级完成,用户在更新表 产品时没有 登录到SSMA并禁用触发器的功能);
  3. 来堆栈溢出,并希望有人已经遇到这种类型的问题!

从概念上讲,这怎么可以做到?

6/7更新:

这里是表A触发代码(例如):

ALTER TRIGGER [dbo].[GRSM_WETLANDS_Point_GIS_tbl_locations_update] 
    ON [dbo].[GRSM_WETLANDS_POINT] 
    after update 
    AS 
    BEGIN 
     SET NOCOUNT ON; 

     update dbo.TBL_LOCATIONS 
     set 
    X_Coord = i.X_Coord, 
    Y_Coord = i.Y_Coord, 
    PlaceName = i.PlaceName, 
    FCSubtype = case 
    when i.FCSubtype = 1 then 'Point: Too Small to Determin Boundary' 
    when i.FCSubtype = 2 then 'Point: Boundary Determined by Contractor but not Surveyed' 
    when i.FCSubtype = 3 then 'Point: Wetland Reported but not yet Surveyed' 
    end , 
    Landform = i.Landform 

    from dbo.TBL_LOCATIONS 
    Join inserted i 
    on TBL_LOCATIONS.GIS_Location_ID = i.GIS_Location_ID 
     end 



GO 

而且

ALTER TRIGGER [dbo].[GRSM_WETLANDS_POINT_GIS_tbl_locations] 
ON 

[dbo].[GRSM_WETLANDS_POINT] 
after INSERT 
AS 
BEGIN 
SET NOCOUNT ON; 
INSERT dbo.TBL_LOCATIONS(
X_Coord, Y_Coord, 
PlaceName, 
FCSubtype, Landform 
) 

SELECT 
a.X_Coord, a.Y_Coord, 
a.PlaceName, 
a.FCSubtype, a.Landform 

From 
( 
SELECT 
X_Coord, Y_Coord, 
PlaceName, 
FCSubtype = case 
when FCSubtype = 1 then 'Point: Too Small to Determin Boundary' 
when FCSubtype = 2 then 'Point: Boundary Determined by Contractor but not Surveyed' 
when FCSubtype = 3 then 'Point: Wetland Reported but not yet Surveyed' 
end , 
Landform 

FROM inserted 
) AS a 

end 

GO 

这里是餐桌上当前已禁用更新触发器产品:

ALTER TRIGGER [dbo].[tbl_locations_updateto_geo] 
ON [dbo].[TBL_LOCATIONS] 
for update 
AS 

BEGIN 
--IF @@NESTLEVEL>1 RETURN 
    SET NOCOUNT ON; 
    update dbo.GRSM_Wetlands_Point 
    set 
X_Coord = i.X_Coord, 
Y_Coord = i.Y_Coord, 
PlaceName = i.PlaceName, 
FCSubtype = i.FCSubtype, 
Landform = i.Landform, 
from dbo.TBL_LOCATIONS 
Join inserted i 
on TBL_LOCATIONS.GIS_Location_ID = i.GIS_Location_ID 
where TBL_LOCATIONS.FCSubtype = 'Polygon: Determination Made by GPS Survey' 
or TBL_LOCATIONS.FCSubtype = 'Polygon: Determination Derived from NWI' 
or TBL_LOCATIONS.FCSubtype = 'Polygon: Determination Made by Other Means' 
    or TBL_LOCATIONS.FCSubtype = 'Polygon: Legal Jurisdictional Determination'; 
    end 
GO 

(TBL名称变更为保持与发布文字)

+0

假设你必须保留触发器,我可以想象一个涉及每个表的'rowversion'的解决方案,也存储在其他表的每个表中。 – Oded

+1

更新是否需要实时?如果没有,那么一个简单的存储过程来同步表格将是一个更简单的解决方案。您可以按计划进行设置。 –

+0

看看[TRIGGER_NESTLEVEL](http://msdn.microsoft.com/en-us/library/ms182737.aspx?ppud=4)。 – HABO

回答

5

有两种类型的递归,直接和间接的:http://msdn.microsoft.com/en-us/library/ms190739.aspx

可以使用RECURSIVE_TRIGGERS选项停止直接递归,但你的情况是间接递归如此你必须设置嵌套触发器选项。这将解决你的问题,但如果系统中的其他任何东西都依赖于递归,那么它将不是一个好的选择。

USE DatabaseName 
GO 
EXEC sp_configure 'show advanced options', 1 
GO 
RECONFIGURE 
GO 
EXEC sp_configure 'nested triggers', 0 
GO 
RECONFIGURE 
GO 

编辑回应你更新的帖子:

我几乎不想给你这样的解决方案,因为你最终采取了非常糟糕的设计和扩展它......让比更乱它已经不是花时间来了解正在发生的事情,而只是修复它。您应该诚实地创建另一个表来保存需要在两个表之间同步的值,以便数据只在一个位置,然后通过密钥将这些表与那些表相关联。但是......

你需要一个标志来设置你在一个触发器中更新,所以如果另一个触发器看到它是真的,它可以中止它的操作。因为(据我所知),你只能有本地作用域变量,这意味着你需要一个表来存储这个标志值并从中查找它。

您可以使用不同级别的复杂度来实现此解决方案,但最简单的方法是让所有触发器在启动时将标志设置为true,并在结束时将标志设置为false。在他们开始之前,他们检查标志并且如果它是真的就停止执行;

这样做的问题是,可能会有另一个更新与同时发生的触发器无关,并且不会传播到下一个表。如果你想采用这条路线,那么我会让你知道如何解决这个问题。

+0

虽然我会说如果系统中的其他东西依赖嵌套触发器,那么它应该尽可能重新设计。总的来说,我尽可能避开触发器。我偶尔使用它们来完成非常非常简单的任务;但是当它们破裂时它们可能是一个真正的痛苦,而且你甚至不知道它们在那里,并且错误消息中没有任何内容会给你提示触发器导致问题的任何提示。 –

+0

全部,谢谢你的回答!我尝试了所有的东西,在嵌套级别函数上安定下来,但仍然无法实现。我编辑原始帖子以包含当前存在的触发器语法。 – tpcolson

+0

@tpcolson更新了我的回答 –