Encountering error when using update trigger with transactional replication - sql-server

I'm using transactional replication with updatable subscription.
when I add a trigger for update in one of my tables which is chosen for replication in publisher, I encounter with this error:
Maximum stored procedure, function,trigger,
or view nesting level exceeded(limit 32)
My trigger code is
create trigger Isupdated
on tbl_worker
for update as
update tbl_worker SET
Isup=1
where id= (select id from inserted)
what's wrong?

It looks like you've written a recursive (aka nested) trigger.
Perhaps the trigger is causing an update on the table, causing the trigger to be fired again?
If you post the code, that would help us explain exactly what the issue is.

http://www.sqlmonster.com/Uwe/Forum.aspx/sql-server-programming/4752/Maximum-stored-procedure-function-trigger-or-view
The above link provides a solution to trigger nesting. This can be very helpful when you already have a trigger and then replication adds another one. I like this more than combining the triggers because it doesn't force you to mix code related to functionality and code related to replication.
To sum up the solution:
You can prevent the nested firing from happening by assigning the triggers
an order with sp_settriggerorder and add the following check to the
beginning of the trigger you set to fire first to prevent it being fired by
the other trigger:
CREATE TRIGGER....
IF TRIGGER_NESTLEVEL > 1 RETURN

Related

Is it possible to get notified when a new table trigger is added in Sql Server?

For example, when tables are modified we get notified through the DDL triggers. However, these triggers are not invoked when a trigger is added for a table.
Is there another kind of a trigger for that? Or some other way we can be notified if that?
I can, of course, take a snapshot of the sys.triggers view with parent_class = 1 before and after the SQL code is applied and compare, but I am curious if there is a better way.
EDIT 1
I wonder what is the meaning of the CREATE_TRIGGER value in the sys.trigger_event_types view...

changetable doesn't have current changes when in a trigger?

I have the following trigger to save the changes to a log table. However, it will not catch the changes triggered the trigger? Or is there another solution?
alter trigger trigger_xxx on table1 after delete, update, insert
as
begin
declare #lastVersion bigint = coalesce((select max(SYS_CHANGE_VERSION) from [log]), 0)
insert into [log]
([SourceColumnDescriptionPattern], SYS_CHANGE_VERSION, SYS_CHANGE_OPERATION, SYS_CHANGE_COLUMNS, SYS_CHANGE_CONTEXT)
SELECT [SourceColumnDescriptionPattern], SYS_CHANGE_VERSION, SYS_CHANGE_OPERATION, SYS_CHANGE_COLUMNS, SYS_CHANGE_CONTEXT
FROM changetable(changes [table1], #lastVersion) as ct
end
Change Tracking is intended for synchronization purposes. For example you can use it to find out if a application side cache needs to be refreshed. Therefore you do not want for that information to show up before the transaction is committed. As your trigger executes within the transaction the changes are not visible yet.
Why are you trying to duplicate the information available in Change Tracking? Cant you just use those functions and DMVs instead of your own?
Assuming you have a good reason, your best bet is probably to use a trigger and capture the affected primary key together with other pertinent information like a timestamp yourself. However there is no real good way to enforce that trigger being executed before all others so you might still end up in the same situation. You could try sp_settriggerorder in your case: http://msdn.microsoft.com/en-us/library/ms186762.aspx It might be enough in your situation.

When do triggers fire and when don't they

Pretty general question regarding triggers in SQL server 2005.
In what situations are table triggers fired and what situations aren't they?
Any code examples to demonstrate would be great.
I'm writing a audit based databases and just want to be aware of any situations that might not fire off the triggers that I have set up for update, delete and insert on my tables.
A example of what I mean,
UPDATE MyTable SET name = 'test rows' WHERE id in (1, 2, 3);
The following statement only fires the update trigger once.
When do you want them to fire?
CREATE TRIGGER AFTER ACTION
That runs after the action (insert update delete) being committed. INSTEAD OF fires the trigger in place of the action.
One of the biggest gotchas with triggers is that they fire whenever an action is performed, even if no rows are affected. This is not a bug, and it's something that can burn you pretty quickly if you aren't careful.
Also, with triggers, you'll be using the inserted and deleted tables. Updated rows are listed in both. This throws a lot of folks off, because they aren't used to thinking about an update as a delete then insert.
The MSDN documentation actually has a pretty in-depth discussion about when triggers fire and what effect they have here.
On 2008 you can use built in Change Data Capture
Also There are quite a few situations when triggers do not fire, such as:
· A table is dropped.
· A table is truncated.
· Settings for nested and/or recursive triggers prevent a trigger from firing.
· Data is bulk loaded, bypassing triggers.
The following statement only fires the update trigger once.
Any action type statement only fires the trigger once no matter how many rows are affected, triggers must be written to handle multiple row inserts/updates/deletes.
If your trigger depends on only one row at a time being in the inserted or deleted pseudotables, it will fail. And worse it will not fail with an error, it will simply not affect all the rows you want affected by whatever the trigger does. Do not fix this through a loop or a cursor in a trigger, change to set-based logic. A cursor in a trigger can bring your entire app to a screeching halt while a transaction of 500,000 records processes and locks up the table for hours.
Bulk inserts by pass triggers unless you specify to use them. Be aware of this because if you let them by pass the trigger you will need code to make sure whatever happens in the trigger also happens after the bulk insert. Or you need to call the bulk inserts with the FIRE_TRIGGERS option.
I thought I'd highlight from the link Eric posted a situation in which a trigger would not fire:
Although a TRUNCATE TABLE statement is in effect a DELETE, it cannot activate a trigger because the operation does not log individual row deletions. However, only those with permissions on a table to execute a TRUNCATE TABLE need be concerned about inadvertently circumventing a DELETE trigger with a TRUNCATE TABLE statement.

How can I find out when a Sql Database field gets modified?

One of my database fields in a table is getting modified by some piece of code. I just can't figure out where!
So, I was wondering if there's a way I can find out.
I'm using SQL 2008. Can Profiler be used to find out if a particular field is getting updated? How?
What about a Trigger? If using a trigger (eg. on UPDATE) can you determine what code called it? How can the trigger 'notify me' of this? Email/file?
Yes, an "AFTER UPDATE" trigger on that particular table and field might give you some clues as to when and why the field gets changed.
From Books Online:
CREATE TRIGGER reminder
ON Person.Address
AFTER UPDATE
AS
IF ( UPDATE (StateProvinceID) OR UPDATE (PostalCode) )
BEGIN
RAISERROR (50009, 16, 10)
END;
GO
A trigger can execute basically any T-SQL code - if you have database mail set up correctly, it could send you an e-mail, yes. Or it can write an audit entry into another table or something like that.
EDIT: If you need to find out which statements updated your column, you might be actually better off running a trace on the server, limited to that specific table, and just trace what's happening there. I don't think a trigger can give you that information (which code caused the update to happen).
Marc
Determining the last Update to or Select against a table (without a trigger!)
http://sqlblogcasts.com/blogs/tonyrogerson/archive/2007/06/03/determining-the-last-update-to-or-select-against-a-table-without-a-trigger.aspx
Yes, you can use a trigger to execute some code (keep track of who updated the table, email you, etc.) when a table is updated. See this link: Track Updates with a Database Trigger
edit: originally posted the wrong link

Is there a way to disable a SQL Server trigger for just a particular scope of execution?

In SQL Server 2005, is there a way for a trigger to find out what object is responsible for firing the trigger? I would like to use this to disable the trigger for one stored procedure.
Is there any other way to disable the trigger only for the current transaction? I could use the following code, but if I'm not mistaken, it would affect concurrent transactions as well - which would be a bad thing.
DISABLE TRIGGER { [ schema_name . ] trigger_name [ ,...n ] | ALL } ON { object_name | DATABASE | ALL SERVER } [ ; ]
ENABLE TRIGGER { [ schema_name . ] trigger_name [ ,...n ] | ALL } ON { object_name | DATABASE | ALL SERVER } [ ; ]
If possible, I would like to avoid the technique of having a "NoTrigger" field in my table and doing a NoTrigger = null, because I would like to keep the table as small as possible.
The reason I would like to avoid the trigger is because it contains logic that is important for manual updates to the table, but my stored procedure will take care of this logic. Because this will be a highly used procedure, I want it to be fast.
Triggers impose additional overhead on the server because they initiate an implicit transaction. As soon as a trigger is executed, a new implicit transaction is started, and any data retrieval within a transaction will hold locks on affected tables.
From: http://searchsqlserver.techtarget.com/tip/1,289483,sid87_gci1170220,00.html#trigger
I just saw this article recently highlighted on the SQL Server Central newsletter and it appears to offer a way which you may find useful using the Context_Info on the connection:
http://www.mssqltips.com/tip.asp?tip=1591
EDIT by Terrapin:
The above link includes the following code:
USE AdventureWorks;
GO
-- creating the table in AdventureWorks database
IF OBJECT_ID('dbo.Table1') IS NOT NULL
DROP TABLE dbo.Table1
GO
CREATE TABLE dbo.Table1(ID INT)
GO
-- Creating a trigger
CREATE TRIGGER TR_Test ON dbo.Table1 FOR INSERT,UPDATE,DELETE
AS
DECLARE #Cinfo VARBINARY(128)
SELECT #Cinfo = Context_Info()
IF #Cinfo = 0x55555
RETURN
PRINT 'Trigger Executed'
-- Actual code goes here
-- For simplicity, I did not include any code
GO
If you want to prevent the trigger from being executed you can do the following:
SET Context_Info 0x55555
INSERT dbo.Table1 VALUES(100)
If your trigger is causing performance problems in your application, then the best approach is to remove all manual updates to the table, and require all updates to go through the insert/update stored procedures that contain the correct update logic. Then you may remove the trigger completely.
I suggest denying table update permissions if nothing else works.
This also solves the problem of duplicate code. Duplicating code in the update SP and in the trigger is a violation of good software engineering principles and will be a maintenance problem.
ALTER TABLE tbl DISABLE TRIGGER trg
http://doc.ddart.net/mssql/sql70/aa-az_5.htm
I don't understand the meaning of your 1st paragraph though
Since you indicate that the trigger contains logic to handle all updates, even manual updates, then that should be where the logic resides. The example you mention, wherein a stored procedure "will take care of this logic" implies duplicate code. Additionally, if you want to be sure that every UPDATE statement has this logic applied regardless of author, then the trigger is the place for it. What happens when someone authors a procedure but forgets to duplicate the logic yet again? What happens when it is time to modify the logic?
Not sure if this is a good idea but it seems to work for me. Transaction should prevent inserts to the table from other processes while trigger is disabled.
IF OBJECT_ID('dbo.TriggerTest') IS NOT NULL
DROP PROCEDURE dbo.TriggerTest
GO
CREATE PROCEDURE [dbo].[TriggerTest]
AS
BEGIN TRANSACTION trnInsertTable1s
;
DISABLE TRIGGER trg_tblTable1_IU ON tblTable1
;
BEGIN -- Procedure Code
PRINT '##trancount'
PRINT ##TRANCOUNT
-- Do Stuff
END -- Procedure Code
;
ENABLE TRIGGER trg_tblTable1_IU ON tblTable1
IF ##ERROR <> 0 ROLLBACK TRANSACTION
ELSE COMMIT TRANSACTION
Do not disable the trigger. You are correct that will disable for any concurrent transactions.
Why do you want to disable the trigger? What does it do? WHy is the trigger casuing a problem? It is usually a bad idea to disable a tigger from a data integrity perspective.
Consider rewriting the trigger to imporve performance if performance is the issue.
I waffled a bit on this one. On the one hand I'm very anti-trigger mostly because it's one more place for me to look for code executing against my table, in addition to the reasons stated in the article linked in the question post.
On the other hand, if you have logic to enforce stable and immutable business rules or cross-table actions (like maintaining a history table) then it would be safer to get this into a trigger so procedure authors and programmers don't need to deal with it - it just works.
So, my recommendation is to put the necessary logic in your trigger rather than in this one proc which, will inevitably grow to several procs with the same exemption.
I just confronted the same problem and came up with the following solution, which works for me.
Create a permanent DB table that contains one record for each trigger that you want to disable (e.g. refTriggerManager); each row contains the trigger name (e.g. strTriggerName = 'myTrigger') and a bit flag (e.g. blnDisabled, default to 0).
At the beginning of the trigger body, look up strTriggerName = 'myTrigger' in refTriggerManager. If blnDisabled = 1, then return without executing the rest of the trigger code, else continue the trigger code to completion.
In the stored proc in which you want to disable the trigger, do the following:
BEGIN TRANSACTION
UPDATE refTriggerManager SET blnDisabled = 1 WHERE strTriggerName = 'myTrigger'
/* UPDATE the table that owns 'myTrigger,' but which you want disabled. Since refTriggerManager.blnDisabled = 1, 'myTrigger' returns without executing its code. */
UPDATE refTriggerManager SET blnDisabled= 0 WHERE triggerName = 'myTrigger'
/* Optional final UPDATE code that fires trigger. Since refTriggerManager.blnDisabled = 0, 'myTrigger' executes in full. */
COMMIT TRANSACTION
All of this takes place within a transaction, so it's isolated from the outside world and won't affect other UPDATEs on the target table.
Does anyone see any problem with this approach?
Bill
I concur with some other answers. Do not disable the trigger.
This is pure opinion, but I avoid triggers like the plague. I have found very few cases where a trigger was used to enforce database rules. There are obvious edge cases in my experience, and I have only my experience on which to make this statement. I have typically seen triggers used to insert some relational data (which should be done from the business logic), for insert data into reporting table ie denormalizing the data (which can be done with a process outside the transaction), or for transforming the data in some way.
There are legitimate uses for triggers, but I think that in everyday business programming they are few and far between. This may not help in your current problem, but you might consider removing the trigger altogether and accomplishing the work the trigger is doing in some other fashion.
you can use 'Exec' function to diable and enable triggers from a stored procedure. Example: EXEC ('ENABLE TRIGGER dbo.TriggerName on dbo.TriggeredTable')

Resources