Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
If a Table A is updated a trigger gets fired. That trigger calls another SP to do some processing.
Is there any chance that if SP fails the update that happened on Table A will be reverted?
I have a code just after the update "If Sqlca.SqlCode" and this always has 0 coming for the update.
Please Help!!
Yes, if the trigger encounters an error (internally or through calling some external procedure) and rolls back the transaction, it will roll back the whole transaction, including whatever UPDATE caused the trigger to fire in the first place. There are multiple ways to get around this, if it is not the behavior you want:
use TRY / CATCH to absorb any errors form the external procedure, or move the procedure logic into the trigger, or add proper error handling to the stored procedure so that, if you don't care that an error happened there, it doesn't bubble up and roll back everything.
use an INSTEAD OF trigger - combined with TRY / CATCH (or possibly committing your own UPDATE first), you should be able to update the table without caring whether the external stored procedure fails.
Example of the INSTEAD OF trigger:
USE tempdb;
GO
CREATE TABLE dbo.flooblat(id INT PRIMARY KEY, name VARCHAR(32));
INSERT dbo.flooblat(id,name) VALUES(1, 'Bob');
GO
CREATE PROCEDURE dbo.oh_my
AS
SELECT 1/0;
GO
CREATE TRIGGER dbo.trFlooblat
ON dbo.flooblat
INSTEAD OF UPDATE
AS
BEGIN
SET NOCOUNT ON;
UPDATE f SET f.name = i.name
FROM dbo.flooblat AS f
INNER JOIN inserted AS i
ON f.id = i.id;
COMMIT TRANSACTION;
EXEC dbo.oh_my;
END
GO
UPDATE dbo.flooblat SET name = 'Frank';
GO
SELECT id, name FROM dbo.flooblat;
GO
Results:
Msg 8134, Level 16, State 1, Procedure oh_my
Divide by zero error encountered.
The statement has been terminated.
However, the SELECT reveals that, even though an error occurred in the trigger, it happened after the UPDATE was committed - so unlike an exception that occurs in an AFTER trigger (without proper error handling), we were able to prevent the error from rolling back all of the work we've done.
id name
---- -----
1 Frank
Triggers can do things AFTER the DML is executed, INSTEAD OF executing the DML, etc. So yes, there is a chance that the update (dml) wont happen if the SP fails -- just depends on how you write it / what features you use.
Read up on triggers a bit here: http://technet.microsoft.com/en-us/library/ms189799%28v=sql.105%29.aspx
If you want a more specific answer for trigger in quesiton, then you'll need to post the code.
Related
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 5 years ago.
Improve this question
We can go for transaction management in procedure but we can’t go in function, I have seen this statement at multiple places, while we ask for difference between function & procedure, But I did below test in oracle and **I can see its working fine for function also**. Can anybody please let me know what thing am I missing about above statement, because this statement looks completely wrong to me?
select * from test; *(test table having single column "name varchar(2)")
create or replace function FUNTest
return number as result NUMBER(6,2);
BEGIN
SAVEPOINT fn_fntest;
insert into test(NAME) values('Dinesh');
ROLLBACK TO fn_fntest;
return 1;
END;
/
Begin
DBMS_OUTPUT.PUT_LINE(FUNTest());
end;
/
Purpose of function is different from procedure.
Function: Supposed to do some calculation and return some value(most
of the cases)
Procedure: Perform some operation based on data/column.It manages transaction as well, because you will definately be storing new data somewhere.
Now talking about transaction management in function, well it depends on calling mechanism of function.
If your function is having transactional statement like commit/rollback then it should be called from some other block which is capable of handling transaction like procedure or anonymous block(your case).
If you call that same function from select statement like "select funtest() from dual;" then you will get error as select statement is not capable of opening transaction.
If you still want to call any function having transactional statement from non-transactional body(select statement) then your function should be capable of opening separate independent transaction(PRAGMA AUTONOMOUS_TRANSACTION).
Please refer to http://www.datacoons.com/content/transaction.php for more information on transaction management.
I am trying to do a trigger, that select values into some tables, and then insert them into another table.
So, for now I got this. There is a lot of columns, so I don't copy them, it is only varchar2 values, and this part works, so I don't think it is useful :
create or replace
TRIGGER TRIGGER_FICHE
AFTER INSERT ON T_AG
BEGIN
declare
begin
INSERT INTO t_ag_hab#DBLINK_DEV
()
values
();
/*commit;*/
end;
END;
Stored procedure where trigger will be called(again a lot of parameters, not relevant to copy them :
INSERT INTO T_AG()
VALUES
();
commit work;
The thing is, we cannot do commit into a trigger, I read that, and understand it.
But, how can I see an update of my table, with the new value?
When the process is runnign, there is nor error, but I don't see the new line into t_ag_hab.
I know it's not very clear, but I don't know how to explain it other way.
How can I fix this?,
Because you're inserting into a remove table via a database link, you have a distributed transaction:
... distributed transaction processing is more complicated because the database must coordinate the committing or rolling back of the changes in a transaction as an atomic unit. The entire transaction must commit or roll back.
When you commit, you're committing both the local insert and the remote insert performed by your trigger, as an atomic unit. You cannot commit one without the other, and you don't have to do anything extra to commit the remote change:
The two-phase commit mechanism is transparent to users who issue distributed transactions. In fact, users need not even know the transaction is distributed. A COMMIT statement denoting the end of a transaction automatically triggers the two-phase commit mechanism. No coding or complex statement syntax is required to include distributed transactions within the body of a database application.
If you can't see the inserted data from the remote database afterwards then something else has deleted it after the commit, or more likely you're looking at the wrong database.
One slight downside (though also a feature) of a database link is that it hides the details of where the work is being done. You can drop and recreate a link to make your code update a different target database without having to modify the code itself. But that means your code doesn't know where the insert is actually going - you'd need to check the data dictionary to see where the link is pointing. And even then you might not know as the link can be using a TNS alias to identify the database, and changes to the tnsnames.ora aren't visible from within the database.
If you can see the data after committing by querying t_ag_ab#dblink_dev from the same database you ran your procedure, but you can't see if when queried locally from the database you expect that to be pointing to, then the link isn't pointing where you think it is. The insert is going to one database, and you are performing your query against a different one. Only you can decide which is the 'correct' database though; and either redefine the link (or TNS entry, if appropriate), or change where you're doing the query.
I am not able to understand you requirement clearly. For updating records in main table and insert the old records in audit table. we can use the below query as a trigger.(MS-SQL)
Create trigger trg_update ON T_AGENT
AFTER UPDATE AS
BEGIN
UPDATE Tab1
SET COL1 = I.COL1, COL2=I.COL2
FROM INSERTED I INNER JOIN Tab1 ON I.COL3=Tab1.Col3
INSERT Tab1_Audit(COL1,COL2,COL3)
SELECT Tab1 FROM DELETED
RETURN;
END;
So far what you presented is just for Inserting trigger. If you want to see the update action done try to add Update like this example.
SQL> CREATE OR REPLACE TRIGGER validate_update
2 AFTER INSERT OR UPDATE ON T_AGENT
3 FOR EACH ROW
4 BEGIN
5 IF UPDATING('ACCOUNT_ID') THEN -- do something like this when updating
6 DBMS_OUTPUT.put_line ('ERROR'); -- add your action here
7 ELSIF INSERTING THEN
8 INSERT INTO t_ag_hab#DBLINK_DEV() values();
9 END IF;
10 END;
11 /
Trigger created.
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
So let me first admit that I am a SQL Server newbie.
Here's the deal: I'm trying to create a trigger on a table in SQL Server 2012, and whenever I try any kind of SELECT statement in the trigger, the table quits working (as in NOTHING can be inserted until the trigger is deleted). As soon as I drop the trigger, everything starts working again. If I don't do any SELECTs, everything is peachy. Is there a permission or something somewhere that I'm missing?
Example:
CREATE TRIGGER sometrigger
ON sometable
FOR INSERT
AS
BEGIN
SELECT * FROM inserted
END
GO
Command completes successfully, but the table becomes frozen as described above.
CREATE TRIGGER sometrigger
ON sometable
FOR INSERT
AS
BEGIN
EXEC msdb.dbo.sp_send_dbmail
#recipients = N'someaddress#somedomain.com',
#subject = 'test',
#body = 'test body',
#profile_name = 'someprofile'
END
GO
Works like a charm.
You're may be falling foul of the disallow results from triggers option being set to 1, as it should be.
Note the warning on that page:
This feature will be removed in the next version of Microsoft SQL Server. Do not use this feature in new development work, and modify applications that currently use this feature as soon as possible. We recommend that you set this value to 1.
I suspect that wherever you're running your inserts from is hiding an error message or exception, since you should get:
Msg 524, Level 16, State 1, Procedure , Line
"A trigger returned a resultset and the server option 'disallow_results_from_triggers' is true."
Or, in the alternative, you're working with a database layer that wraps all inserts in a transaction and will roll the transaction back if anything unexpected happens - such as receiving a result set or even just an extra information message saying (x rows affected).
But all of this is dancing around the main issue - you shouldn't be issuing a select that attempts to return results from inside of a trigger. I might have been able to offer more help if you'd actually told us what you're trying to achieve.
If it's the second case, and it's something tripping over the (x rows affected) messages, that can be cured by placing SET NOCOUNT ON at the top of the trigger.
You should never return data from a trigger anyway, mainly for simplicity and maintenance reasons. It's confusing: I did an INSERT but get a resultset back.
If you need to get the values you just inserted, you'd use the OUTPUT clause
INSERT sometable (...)
OUTPUT INSERTED.*
VALUES (...);
This at least tells you that the INSERT gives results.
And it is nestable too as per, say, SQL Server concurrent transaction issue
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.