4

我知道我会因此而发火,但是......

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

我希望在表产品上开发一个触发器,它将 A 列中的更新传播到每个表 A、B 和 C 中的 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

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

4

1 回答 1

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

这样做的问题是,可能有另一个与同时发生的触发器无关的更新,并且它不会传播到下一个表。如果你想走这条路,那么我会让你自己想办法解决这个问题。

于 2012-06-02T20:03:44.517 回答