4

I know I'm going to get flamed for this, but....

I have table ProductA, ProductB, and ProductC which have very similar schema but for 2 or 3 columns in each. Each table has an insert trigger which fires a duplicate row for each insert in A, B, or C to table Products, which is a consolidation of all products. In addition, update triggers on A,B, or C will likewise update their equivalent row in Table Products, as do delete triggers. All working flawlessly until.....we update, say, Table Products Column A, which also exists in Table A, B, and C.

I'm looking to develop a trigger on Table Products that will propogate that update in Column A to Column A in each of tables A, B, and C, BUT, without invoking the update triggers on Tables A, B, and C. The desired behavior is for updates to work in both directions without incurring an endless loop.(Note, only 2 columns in table products need to be replicated BACK to tables A, B, and C)

Options are:

  1. redesign the schema so this situation doesn't exist (not in the cards, this is a quick solution, redesign can be done by someone else);
  2. Manually disable the triggers when I update table products (this is all done at the application level, users won't have the ability to log into SSMA and disable triggers when they update table products);
  3. Come to Stack Overflow and hope someone has already encountered this type of problem!

Conceptually, how could this be done?

6/7 Update:

Here is the trigger code on Table A (e.g):

    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

And

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

And here is the currently disabled update trigger on table products:

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 names changed to keep with the posting text)

tpcolson
  • 716
  • 1
  • 11
  • 27
  • Assuming you must keep the triggers, I can imagine a solution involving `rowversion` for each table, stored in each table for the other tables as well. – Oded Jun 02 '12 at 19:27
  • 1
    Do the updates need to be real-time? If not, then a simple stored proc to sync the tables up would be a much simpler solution. You could set it on a schedule. – Brandon Moore Jun 02 '12 at 20:04
  • Have a look at [TRIGGER_NESTLEVEL](http://msdn.microsoft.com/en-us/library/ms182737.aspx?ppud=4). – HABO Jun 02 '12 at 21:43
  • Another approach, using [CONTEXT_INFO](http://msdn.microsoft.com/en-us/library/ms180125.aspx) to talk to yourself, is described in the accepted answer to [this](http://stackoverflow.com/questions/2237499/sql-server-trigger-loop) SO question. – HABO Jun 02 '12 at 21:48

1 Answers1

5

There are two types of recursion, direct and indirect: http://msdn.microsoft.com/en-us/library/ms190739.aspx

You can use the RECURSIVE_TRIGGERS option to stop direct recursion, but your case is indirect recursion so you'd have to set the nested triggers option. This will fix your problem, but if anything else in the system relies on recursion then it won't be a good option.

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

EDIT in response to your updated post:

I almost hate to give you this solution because you're ultimately taking a really crappy design and extending it... making even more of a mess than it is already instead of taking the time to understand what's going on and just fixing it. You should honestly just create another table to hold the values that need to be in sync between the two tables so the data is only in one place, and then relate those tables to that one through a key. But nonetheless...

You need a flag to set you're updating in one trigger so the other trigger can abort its operation if it sees it's true. Since (as far as I know) you can only have locally scoped variable, that means you'll need a table to store this flag value in and look it up from.

You can implement this solution with varying levels of complexity, but the easiest way is to just have all triggers set the flag to true when starting and false when ending. And before they start they check the flag and stop executing if it's true;

The problem with this is that there could be another update that isn't related to a trigger happening at the same time and it wouldn't get propogated to the next table. If you want to take this route, then I'll leave it up to you to figure out how to solve that problem.

Brandon Moore
  • 8,590
  • 15
  • 65
  • 120
  • Although I'd say if anything else in the system relies on nested triggers then it should probably be redesigned if possible. In general, I try to steer clear of triggers whenever possible. I've used them on occasion for very, very simple tasks; but they can be a real pain when they break and you don't even know they're there and nothing in the error messages gives you any hint that a trigger is causing the problem. – Brandon Moore Jun 04 '12 at 05:52
  • All, thanks for the answers! I tried everything, settling on the nested level function, but still could not get it to work. I edited the original post to include the trigger sytax that currently exists. – tpcolson Jun 07 '12 at 21:51
  • An exception to my edited answer would be if GRSM_Wetlands_Poly is a view that includes GRSM_WETLANDS_POINT. I am doubting that's the case but if it is let me know and I will look it over again. – Brandon Moore Jun 07 '12 at 22:58
  • Brandon Moore updated my post, you were right, something was odd. I was copying and pasting and fat fingered the text. – tpcolson Jun 08 '12 at 11:30