I am looking for a good way to log changes that occur on a particular set of tables in my SQL Server 2005 database. I believe the best way to do this is through a trigger that get's executed on updates and deletes. Is there anyway to grab the actual statement that is being ran? Once I have the statement I can easily log it someplace else (other DB table). However, I have not found an easy way (if possible) to grab the SQL statement that is being ran.
相关问题
- SQL join to get the cartesian product of 2 columns
- sql execution latency when assign to a variable
- Difference between Types.INTEGER and Types.NULL in
- What is the best way to cache a table from a (SQL)
- php PDO::FETCH_ASSOC doesnt detect select after ba
There is a pattern for creating such these triggers called Log Trigger. This is vendor independent and very simple. It is described in here.
The changes are recorded on another history table. There is no way to grab the exact statement, but it is possible to detect if it was an insertion, and update or a deletion because it creates a "chained" set of records. An insertion is a record with no predecessor, a deletion is a record with no successor, intermediate records are updates. Changes can be detected comparing a record against its predecessor.
It is very easy to get a snapshot of a single entity (or the whole table) at a given point of time.
As a bonus, the syntax of this pattern for SQL Server happens to be the simplest one, compared to Oracle, DB2 and MySQL.
If you just want to keep a log of all transactions (insert, update and delete) in some database tables, then you can run the following script:
Don't forget that your logging will be part of the transaction so if there is an error and you rollback the transaction, your log will also be deleted.
Try installing some trigger based third party tool such as ApexSQL Audit and then reverse engineering how they do it. Just install it in trial mode and see how it generates triggers for capturing all different sorts of information.
Several other things to consider are:
Storage planning – if you have a lot of updates that means you’ll have a ton of audit data. I’d consider storing that data in separate databases. Especially if you plan on auditing more than one database.
Managing the amount of data – over time you probably won’t be in a need to keep some very old records. Plan on easy deletion of old data
Schema changes – what if schema is updated. In worst case your triggers will stop working and throw an error if not created correctly. At best case you’ll miss some of the data. This is also something to consider.
Taking all of this into account it is probably the most time effective to go with some already developed solution vs creating this from scratch yourself.
Triggers are a good way to ensure that any changes are logged, since they will almost always fire regardless of how the updates are performed - e.g. ad-hoc connections as well as application connections.
As suggested by @mwigdahl, the system management views look like a good way to capture the current running batch. Whether that's particularly useful to log in the trigger is another thing.
One downside to using triggers is that you can only identify the source of the update from the database connection. Many applications don't have any user information associated with the connection, to facilitate connection pooling, so you don't know which user is performing the action. ie the Login used by the connection is a generic application login rather than the person using the application. The normal way to get around this is to use stored procedures as the interface to all database interaction, and then ensure that a UserId is passed with all procedure calls. You can then perform your logging via the stored procedure instead of a trigger. Clearly this is only useful if you know people won't update tables directly without using the procedures, or don't need to log that situation.
The ability to get the currently executing batch might provide an even better mechanism: if you ensure that all your sql batches contain a UserId you could then extract this from the sql within your trigger. That would allow you to do all logging with triggers, which means you capture everything, but also let you associate changes with a particular user.
If you're going down the trigger route it's worth checking the situations triggers aren't fired (maybe bulk loaded data? or if people have permission to disable triggers).
Also consider as @idstam pointed out that trigger code will be within your transaction so will normally be logged and rolled back along with it.
Another thing to consider when writing triggers is the behaviour of @@IDENTITY: if you have procedures using @@IDENTITY you might accidentally change their behaviour.
Be careful here, since triggers fire at the ROW level, not the SQL STATEMENT level. So, if someone does "DELETE FROM BIGTABLE", your trigger will fire for each row in that table (this specifically in regard to the fact that you want to know the SQL statement that performed the operation, so you'll need to "figure that out" for every row the statement affects).