Skip to content
Advertisement

SQL Server: log database changes through generic trigger

Starting from this article, which is creating a trigger to log insert-, update- and delete-statements within the database, I’d like to create a similar trigger which doesn’t require to define the table and its columns. This would us to reduce human errors when dropping or adding columns.

I’ve had many issues (hence the extra code compared to the article) but wasn’t able to overcome accessing the table columns through a stringified name within the trigger.

The current code creates a row with the same values for COLUMN_NAMES, COLUMN_OLD_VALUES, and COLUMN_NEW_VALUES because the code that populates the @column_old_values and @column_new_values actually executes SELECT 'column_name' FROM AnyTable instead of SELECT columnn_name FROM MyTableName but I can’t seem to figure out on how to solve this.

I’ve tried using the EXEC() command, but that seems to partially end my trigger? Even though the code compiles, I continue to receive an error about unclosed quotation marks.

enter image description here

Msg 102, Level 15, State 1, Line 16
Incorrect syntax near ‘=’.

Msg 105, Level 15, State 1, Line 16
Unclosed quotation mark after the character string ‘) + ColumnName1 FROM deleted’.

I think this generic trigger works as soon as this issue is solved but of course, I’d be happy with alternative solutions as well.

Advertisement

Answer

Some errors with your current code include:

  • An error when 0 rows are affected, because all the tables would be empty but you did not handle a NULL command, and it generates an error when attempting to insert a NULL command into ChangeLog

  • Your cursor would string all affected rows into a strange fashion; even if you got it to work, if 4 rows were affected, you would have 1 row in your ChangeLog where the column_old_values would hold something like (col1, col1, col1, col1, col2, col2, col2, col2).

  • Your cursor would need dynamic SQL to use dynamic column names, but dynamic SQL is in a different scope compared to your code, so you need to make a copy of the inserted and deleted trigger-scope tables to use dynamic SQL.

  • Your dynamic SQL is trying to use variables that don’t exist in the different scope. It’s a lot easier to debug dynamic SQL if you put it into a string, then print the string for review before attempting to EXEC it.

EDIT:

How about this option, which doesn’t rely on knowing the columns but does rely on knowing the table PK’s beforehand? Those shouldn’t change nearly as often as other columns, and the performance for this is vastly superior to what you were trying to do. This is a sample from one I’ve implemented on a table we weren’t sure was still being utilized by one of our few dozen users, and I needed to track it over a year.

The result is perfectly functional for whatever you need:

results of ssms

If you’re willing to change the column types for COLUMN_OLD_VALUES and COLUMN_NEW_VALUES to XML, you can simply add , type right after each for xml path('') and the XML is click-able and easy to read in SSMS.

User contributions licensed under: CC BY-SA
3 People found this is helpful
Advertisement