sql-servert-sqltransactionssql-server-2016-localdb

Making a rollback exempt insert


Here's a weird question: Is there a way to make a rollback exempt insert into a table?

Here's the scenario: We have a trigger that does things.

Sometimes, this trigger will call RAISERRROR(). And the outer transaction rolls back.

However, in the trigger, I'd like to insert values into logging table and not have it go away during the rollback, a transaction exempt insert, if you will.


Solution

  • banana catch

    You can catch exceptions with try-catch and pass/throw them to the outer scope, where must exist the same stuff. This, by the way, allows you to collect call-stack. This solution can be implemented if you are working via storedprocs only. Every proc must have a pattern like this:

    begin try
    end try
    begin catch
       if @@trancount > 0
         rollback
    
       insert into <log> (...)
       values (...)
    
       throw
    end catch
    

    So the topmost procedure will successfully insert a row into log table.

    Cons:

    Pros:

    Feedback:

    elusive bandit

    You can build a CLR-assembly that does insert into log table and nothing more... But! It's possible to specify a separate connection to make this assembly work with db via this separate connection. Which means - within a separate scope. So this assembly's method is being called from within a transaction however is being executed regardless this transaction scope.

    So, instead of:

    using(SqlConnection connection = new SqlConnection("context connection=true"))
    

    just specify a regular connection string. After calling it - throw exception with modified ERROR_STATUS to avoid additional logging of same error.

    begin try
    end try
    begin catch
       if @@trancount > 0
         rollback
    
       if @@ERRROR_STATUS != @done_with_logging
         exec asm.log(...)
    
       raiserror @err_msg, @severity, @done_with_logging
    end catch
    

    Cons:

    Pros:

    Feedback:

    knock, knock, admin

    This simple statement (actually just the additional option WITH LOG) will write any error message you want to SQL SERVER event log:

    RAISERROR(...) WITH LOG

    This is not the way SQL SERVER log is should be used, but this is the quickest way to log something important (for issue resolution). Logged event can be viewed in SSMS agent's windows.

    Cons:

    Pros:

    Feedback:

    flying dutchman

    {a place for opinion-based talks about building a system based on DML triggers}

    It seems to me that you don't use stored procs in your project and execute ad hoc queries instead. If you have a backend application with ORM or something like that - write log with it. Moreover, perhaps this backend app is a better place to do things you do inside that trigger.

    If your project is a client-server app without appserver/backend app and all you got is an ad hoc query and the trigger, then there is not much data to log. No call stack (on server side). And it will be hard to identify how did you (user, app) come to this particular exception. So logging on client-side might be more useful in this case.