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

tpc*_*son 4 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和C上的更新触发器.所需的行为是更新在两个方向上工作而不会产生无限循环.(注意,表产品中只有2列需要复制到表A,B和C)

选项包括:

  1. 重新设计架构,以便这种情况不存在(不是卡片,这是一个快速的解决方案,重新设计可以由其他人完成);
  2. 当我更新表产品时手动禁用触发器(这都是在应用程序级别完成的,用户无法在更新表产品时登录SSMA并禁用触发器);
  3. 来Stack Overflow并希望有人已经遇到过这类问题!

从概念上讲,这怎么可能呢?

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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

以下是表产品上当前禁用的更新触发器:

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
Run Code Online (Sandbox Code Playgroud)

(更改了tbl名称以与发布文本保持一致)

Bra*_*ore 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
Run Code Online (Sandbox Code Playgroud)

编辑以回复您的更新帖子:

我几乎不愿意给你这个解决方案,因为你最终采取了一个非常糟糕的设计并扩展它......使它变得更加混乱,而不是花时间去理解发生了什么,只是修复它.你应该诚实地创建另一个表来保存两个表之间需要同步的值,这样数据只在一个地方,然后通过一个键将这些表与那个表相关联.但尽管如此......

您需要一个标志来设置您在一个触发器中进行更新,以便其他触发器可以在它看到它的情况下中止其操作.因为(据我所知)你只能拥有本地范围的变量,这意味着你需要一个表来存储这个标志值并从中查找.

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

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