I've search high and low, and I haven't been able to find a satisfactory answer to my question. Which tends to boil down to how exactly a TRANSACTION works in SQL Server.
Basically, will this do what I think it does.
BEGIN TRAN
DISABLE [my_update_trigger] ON [my_table]
/*.. Do Some Updates ..*/
ENABLE [my_update_trigger] ON [my_table]
COMMIT TRAN
I want to be able to fix some data in a table, without running the update triggers
I have on the table. This is for a web app, so I'd like to make sure that if an update is done on the table from the web app, while I'm doing my work, [my_update_trigger] will still fire for the web app.
The update stuff is ok - the disable enable etc.
DISABLE TRIGGER [my_update_trigger] ON [my_table]
/*.. Do Some Updates ..*/
ENABLE TRIGGER [my_update_trigger] ON [my_table]
Have a look at the msdn page: http://msdn.microsoft.com/en-us/library/ms189748.aspx
On making it session specific though: I'd doubt if that would work - the disable/enabled are DDL rather than DML, ie they act on the database objects rather than the data. I wouldn't have thought this would be in the scope of a Transaction
Semicolons work for me:
BEGIN TRAN
;DISABLE [my_update_trigger] ON [my_table]
/*.. Do Some Updates ..*/
;ENABLE [my_update_trigger] ON [my_table]
COMMIT TRAN
I did some tests and, apparently, your code does exactly what you want it to do. Here is my test protocol:
-- Session 1 -- -- Session 2 --
1. BEGIN TRANSACTION
2. DISABLE TRIGGER [my_update_trigger] ON [my_table]
3. UPDATE my_table SET x = y -- blocks
4. ROLLBACK
-- unblocks *and triggers the trigger*
In addition, SELECT is_disabled FROM sys.triggers where name = 'my_update_trigger'; reveals that the trigger is disabled after step 2 and enabled after step 4.
Thus, my conclusion is:
Yes, enabling/disabling triggers is "global" and not "per session" (as can be seen by querying sys.triggers), but
SQL Server's locking mechanisms ensure that disabling a trigger in a transaction locks the table, so other sessions won't "miss" their trigger.
A word of caution: DISABLE TRIGGER won't trigger an implicit transaction. This is important if you are using (classic) ADO, since Connection.BeginTrans won't start an explicit transaction but will rather SET IMPLICIT_TRANSACTION ON. You need to perform one of the operations listed here before disabling the trigger, or the DISABLE TRIGGER statement will be outside the transaction.
if you want to execute ENABLE TRIGGER Directly From Source :
we can't write :
Conn.Execute "ENABLE TRIGGER trigger_name ON table_name"
instead, we can write :
Conn.Execute "ALTER TABLE table_name DISABLE TRIGGER trigger_name"
Related
Can SQL Server triggers prevent updating records and allow inserting new records?
Thanks,
Przemek
Couldn't this be accomplished by creating a SQL Server user that has INSERT access on the destination tables but not UPDATE access? Create a new user with no permissions, then:
GRANT INSERT ON [blah] TO [user]
I admit, my knowledge of SQL Server triggers is limited, but this seems like it would work.
To expand on INSTEAD OF UPDATE, this trigger will fire for UPDATE commands. INSERT and DELETE commands work as usual; if you wanted to intercept either of those you'd use an INSTEAD OF INSERT or INSTEAD OF DELETE trigger.
The full solution goes something like this:
CREATE TRIGGER myTable_IO_Upd ON myTable INSTEAD OF UPDATE AS
BEGIN
RAISERROR('myTable does not allow UPDATE queries.', 18, 0);
END;
GO
More information on RAISERROR is here. Basically the second argument (18) is the severity, with 18 being the highest severity allowed for user-defined exceptions. The third argument (0) is the "state", which you don't normally need, hence the zero.
One more example: if you wanted to prevent UPDATE and DELETE, you'd use something like this:
CREATE TRIGGER myTable_IO_Upd ON myTable INSTEAD OF UPDATE, DELETE AS
BEGIN
RAISERROR('myTable does not allow UPDATE or DELETE queries.', 18, 0);
END;
GO
To prevent update on table, you can use "INSTEAD OF UPDATE" Trriger
you can check this note "https://msdn.microsoft.com/en-us/library/aa258254%28SQL.80%29.aspx?f=255&MSPPError=-2147217396"
I have created AFTER INSERT TRIGGER
Now if any case if an error occurs while executing Trigger. It should not effect Insert Operation on Triggered table.
In One word if any ERROR occurs in trigger it should Ignore it.
As I have used
BEGIN TRY
END TRY
BEGIN CATCH
END CATCH
But it give following error message and Rolled back Insert operation on Triggered table
An error was raised during trigger execution. The batch has been
aborted and the user transaction, if any, has been rolled back.
Interesting problem. By default, triggers are designed that if they fail, they rollback the command that fired it. So whenever trigger is executing there is an active transaction, whatever there was an explicit BEGIN TRANSACTION or not on the outside. And also BEGIN/TRY inside trigger will not work. Your best practice would be not to write any code in trigger that could possibly fail - unless it is desired to also fail the firing statement.
In this situation, to suppress this behavior, there are some workarounds.
Option A (the ugly way):
Since transaction is active at the beginning of trigger, you can just COMMIT it and continue with your trigger commands:
CREATE TRIGGER tgTest1 ON Test1 AFTER INSERT
AS
BEGIN
COMMIT;
... do whatever trigger does
END;
Note that if there is an error in trigger code this will still produce the error message, but data in Test1 table are safely inserted.
Option B (also ugly):
You can move your code from trigger to stored procedure. Then call that stored procedure from Wrapper SP that implements BEGIN/TRY and at the end - call Wrapper SP from trigger. This might be a bit tricky to move data from INSERTED table around if needed in the logic (which is in SP now) - probably using some temp tables.
SQLFiddle DEMO
You cannot, and any attempt to solve it is snake oil. No amount of TRY/CATCH or ##ERROR check will work around the fundamental issue.
If you want to use the tightly coupling of a trigger then you must buy into the lower availability induced by the coupling.
If you want to preserve the availability (ie. have the INSERT succeed) then you must give up coupling (remove the trigger). You must do all the processing you were planning to do in the trigger in a separate transaction that starts after your INSERT committed. A SQL Agent job that polls the table for newly inserted rows, an Service Broker launched procedure or even an application layer step are all going to fit the bill.
The accepted answer's option A gave me the following error: "The transaction ended in the trigger. The batch has been aborted.". I circumvented the problem by using the SQL below.
CREATE TRIGGER tgTest1 ON Test1 AFTER INSERT
AS
BEGIN
SET XACT_ABORT OFF
BEGIN TRY
SELECT [Column1] INTO #TableInserted FROM [inserted]
EXECUTE sp_executesql N'INSERT INTO [Table]([Column1]) SELECT [Column1] FROM #TableInserted'
END TRY
BEGIN CATCH
END CATCH
SET XACT_ABORT ON
END
I have table with update cursor which do not allow me to do bulk updates on it, but need to do some periodical bulk updates (query in sql job). To do this I want to disable cursor in code (disabling trigger in SSMS can ends with timeout error), but when disabling in query runs until I stop it.
Can I somehow set timeout for disabling trigger in query code (or set timeout for job run)?
Thanks
Another option might be to use CONTEXT_INFO. This allows you to set a sort of global variable that is scoped to the current request. Before executing the bulk update you can set the CONTEXT_INFO to a specific value. The trigger can check for this value and skip execution. This way you don't have to disable the trigger.
You could make disabling and re-enabling the trigger part of the transaction. Then if the update query times out, the disabling of the trigger will be rolled back too.
I thought "after delete" meant that the trigger is not fired until after the delete has already taken place, but here is my situation...
I made 3, nearly identical SQL CLR after delete triggers in C#, which worked beautifully for about a month. Suddenly, one of the three stopped working while an automated delete tool was run on it.
By stopped working, I mean, records could not be deleted from the table via client software. Disabling the trigger caused deletes to be allowed, but re-enabling it interfered with the ability to delete.
So my question is 'how can this be the case?' Is it possible the tool used on it futzed up the memory? It seems like even if the trigger threw an exception, if it is AFTER delete, shouldn't the records be gone?
All the trigger looks like is this:
ALTER TRIGGER [sysdba].[AccountTrigger] ON [sysdba].[ACCOUNT] AFTER DELETE AS
EXTERNAL NAME [SQL_IO].[SQL_IO.WriteFunctions].[AccountTrigger]
GO
The CLR trigger does one select and one insert into another database. I don't yet know if there are any errors from SQL Server Mgmt Studio, but will update the question after I find out.
UPDATE:
Well after re-executing the same trigger code above, everything works again, so I may never know what if any error SSMS would give.
Also, there is no call to rollback anywhere in the trigger's code.
after means it just fires after the event, it can still be rolled back
example
create table test(id int)
go
create trigger trDelete on test after delete
as
print 'i fired '
rollback
do an insert
insert test values (1)
now delete the data
delete test
Here is the output from the trigger
i fired
Msg 3609, Level 16, State 1, Line 1
The transaction ended in the trigger. The batch has been aborted.
now check the table, and verify that nothing was deleted
select * from test
The CLR trigger does one select and
one insert into another database. I
don't yet know if there are any errors
from SQL Server Mgmt Studio, but will
update the question after I find out.
Suddenly, one of the three stopped
working while an automated delete tool
was run on it.
triggers fire per batch/statement not per row, is it possible that your trigger wasn't coded for multi-row operations and the automated tool deleted more than 1 row in the batch? Take a look at Best Practice: Coding SQL Server triggers for multi-row operations
Here is an example that will make the trigger fail without doing an explicit rollback
alter trigger trDelete on test after delete
as
print 'i fired '
declare #id int
select #id = (select id from deleted)
GO
insert some rows
insert test values (1)
insert test values (2)
insert test values (3)
run this
delete test
i fired
Msg 512, Level 16, State 1, Procedure trDelete, Line 6
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
The statement has been terminated.
check the table
select * from test
nothing was deleted
An error in the AFTER DELETE trigger will roll-back the transaction. It is after they are deleted but before the change is committed. Is there any particular reason you are using a CLR trigger for this? It seems like something that a pure SQL trigger ought to be able to do in a possibly more lightweight manner.
Well you shouldn't be doing a select in trigger (who will see the results) and if all you are doing is an insert it shouldn't be a CLR trigger either. CLR is not generally a good thing to have in a trigger, far better to use t-SQL code in a trigger unless you need to do something that t-sql can't handle which is probably a bad idea in a trigger anyway.
Have you reverted to the last version you have in source control? Perhaps that would clear the problem if it has gotten corrupted.
I am using VB.Net 2008 and ADO.Net to do a Batch Update to our Oracle database.
The updates are working, but there is a trigger on the table before the row is updated to enforce a member's termination termination date.
So if I was trying to set the termination date (via the batch update) to 31-Jan-2010 but the member had a claim that was processed on 2-Feb-2010 the trigger would force the termination date to be 2-Feb-2010. However, the trigger is NOT executing when the batch update runs?
Is there any Oracle DB Admin option that would disable Triggers on Batch Update?
A direct path load through SQL*Loader can disable and reenable triggers as described here
What does the batch update do. Maybe if it does a DELETE+INSERT, rather than an UPDATE, then the trigger won't fire.
The alter trigger command can be used to disable a trigger,
ALTER TRIGGER trigger_name DISABLE
This will show the enabled/disabled status of all triggers,
select trigger_name, status from user_triggers