无限触发循环......按设计(!)。如何解决?

时间:2012-06-02 19:16:15

标签: sql sql-server sql-server-2008

我知道我会因此而受到抨击,但是......

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

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

选项包括:

  1. 重新设计架构,因此这种情况不存在(不在 卡,这是一个快速的解决方案,重新设计可以由某人完成 其他);
  2. 更新表格产品时手动禁用触发器 (这都是在应用程序级别完成的,用户不会拥有 能够在更新表时登录SSMA并禁用触发器 产品);
  3. 来Stack Overflow,希望有人已经遇到过这类问题!
  4. 从概念上讲,如何做到这一点?

    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名称以与发布文本保持一致)

1 个答案:

答案 0 :(得分: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。在他们开始之前,他们检查标志并停止执行,如果它是真的;

这个问题是可能存在另一个与同时发生的触发无关的更新,它不会传播到下一个表。如果你想采取这条路线,那么我将由你决定如何解决这个问题。