Continuing from my earlier post Auditing your MySQL Data, Roland has accurately highlighted that my initial post leaves out some important information for auditing. As the original charter was only to keep a history, for the purpose of comparing certain columns, a history was all that was needed.
Providing a history of changes forms the basis of auditing, and in keeping with my post title and intended follow-up, this is the all important second part. However in order to provide true auditing additional information is necessary. This includes:
- When was an operation performed
- What operation was performed, i.e. INSERT, UPDATE and DELETE
- Who performed the operation
Date and operation can be determined via the database, but in order to gather all this information, interaction with the application is necessary to obtain the true user information (This can’t be determined via a trigger)
The issue becomes a greater need for design understanding. What is the purpose of the audit data? How will it be accessed? How complex in maintaining the data do you wish to consider?
One alternative is keep a separate log of audit history. The benefits are a clear and easy way to provide a history of a users’ actions, and can preserve the structure of database table between the base and audit table can remain the same, triggers can remain relatively simply. However if you want to look at the data with audit history, it is better to embed these columns within each table, and triggers have to be customized and maintained in more detail the my original post.
When considering the progression of these points, the design process normally returns to the following conclusion. The following columns are added to the base table.
- A create_timestamp column is added
- A last_update_timestamp column is added
- A last_update_user_id column is added
The create_timestamp is optional from an auditing perspective, because the last_update_timestamp of the first audit row will contain the same value, however experience has shown this column is valuable for other design considerations.
The only remaining issue is the type of operation, INSERT,UPDATE & DELETE. Both INSERT and UPDATE can be inferred, DELETE can not. To maintain the simplicity model, a common approach is to use a BEFORE DELETE trigger to insert an audit record with all the same values of the previous row, with the last_update_timestamp manually set. DELETE can then be determined via a no difference in any updated values.
It ultimate conclusion comes down your application design and needs. For example, your design for example may include a flag or row status for example to indicate deletes which are later cleaned up via a batch process so you don’t really care about the date/time of the actual purging of data. This then negates the need for any DELETE trigger.
Again, thanks to Roland for providing a link to Putting the MySQL information_schema to Use which provides a number of SQL statements that help in the generation of Triggers to support full auditing.
You should be aware that CURRENT_USER normally serves zero purpose if all changes are made via an application user.
At this time, you also have another design consideration. Do you introduce a procedure to re-create the triggers via an automated means for each schema change, or do you manually maintain triggers with schema changes. With each approach, additional checking and verification is necessary to ensure your triggers are correctly configured.
Roland Bouman says
Hi Ronald,
thanks for linking
Still, I have my doubts here:
“To maintain the simplicity model, a common approach is to use a BEFORE DELETE trigger to insert an audit record with all the same values of the previous row, with the last_update_timestamp manually set. DELETE can then be determined via a no difference in any updated values.”
Is this really that much simpler? In order to find out the last version of all deleted rows, you need a join (or subquery) and do a pairwise comparison all column values to figure out if the last two rows happen to be identical. The extra DELETE trigger really seems a load simpler to me, plus it has the added credibility of explicitly logging DELETE instead of relying on an implicit criterion.
kind regards,
Roland
Isn’t it far simpler to just add a DELETE trigger?
Mark Callaghan says
Very nice. CURRENT_USER also serves no use because statement-based replication doesn’t preserve the value of it on slaves.
Cathy says
The link for Putting the MySQL information_schema to Use seems to be broken. Any chance you could put the correct link on please?
Thanks