Best way to implement a last-modified column in Sql Server 2005? - sql-server

How do you implement a last-modified column in SQL?
I know for a date-created column you can just set the default value to getdate(). For last-modified I have always used triggers, but it seems like there must be a better way.
Thanks.

Triggers are the best way, because this logic is intimately associated with the table, and not the application. This is just about the only obvious proper use of a trigger that I can think of, apart from more granular referential integrity.
But I think "last-modified" dates are a flawed concept anyway. What makes the "last-changed" timestamp any more valuable than the ones that came before it? You generally need a more complete audit trail.

The only other way to perform this without using triggers is to disallow any inserts/updates/deletes on the table directly via permissions, and insist all these actions are performed via stored procedures that will take care of setting the modified date.
An administrator might still be able to modify data without using the stored procedures, but an administrator can also disable triggers.
If there are a lot of tables that require this sort of functionality, I would favour triggers as it simplifies the code. Simple, well written and well-indexed auditing triggers are generally not too evil - they only get bad when you try to put too much logic in the trigger.

You can use the keyword DEFAULT, assuming you have a default constraint.
On insert, there is no need to specify a value, You could use the keyword here too.
No trigger and is done in the same write as the "real" data
UPDATE
table
SET
blah,
LastUpdatedDateTime = DEFAULT
WHERE
foo = bar

Using a trigger to update the last modified column is the way to go. Almost every record in the system at work is stamped with an add and change timestamp and this has helped me quite a bit. Implementing it as a trigger will let you stamp it whenever there is any change, no matter how it was initiated.
Another nice thing about a trigger is that you can easily expand it to store an audit trail as well without too much trouble.

And as long as you are doing so, add a field for last_updated_by and update the user everytime the record is updated. Not as good as a real audit table but much better than the date last updated.
A trigger is the only method you can use to do this. And to those who said triggers are evil, no they aren't. They are the best way - by far - to maintain data integrity that is more complex than a simple default or constraint. They do need to written by people who actually know what they are doing though. But then that is true of most things that affect database design and integrity.

Related

Simplified guide on when to use before/after and for each row/statement triggers

Considering Postgres as Dbms:
-Always use Before Trigger unless you need a specific information that can be obtained only after a record has been inserted or in rare cases updated.
-Always use for each statement unless you need a specific information of a specific row.
Is this correct? Or are there situations where what I wrote is not valid?

How Does FakeTable Work with the COLUMNS_UPDATED() function?

I have a 300+ column table and a trigger.
I have 40(ish) "columns of interest" and the rest I don't care about.
The purpose of the trigger is to determine if any of the "columns of interest" were updated.
So: determining the COLUMNS_UPDATED() seems to be the way to go and do some bit-magic with that.
Right now: I'm Faking the 300+ table and ramming in a complete row to force the correct number of columns to ensure COLMNS_UPDATED() gets the correct bytes/bits flagged for columns changed.
My question is: is this unnecessary because the genius of FakeTable encompasses the use of COLUMNS_UPDATED() and it will return the "correct" byte indicators even if I only faked a subset of the columns?
tSQLt.FakeTable reconstructs the table based on the original and always includes all columns. So, after using tSQLt.ApplyTrigger, to move the trigger over to the faked table, COLUMS_UPDATED will work as designed.
That being said, if you encounter a piece of functionality in any external tool or API that you are going to rely on in your product, and you have the feeling that there is a piece of functionality that is not documented well, it is always a good idea to write an exploratory test and check that test in with your other tests.
In this case I would create a wide table on the fly within the test, take the table, create an update trigger on the faked table and then call tSQLt.AssertEquals within that trigger to make sure the right value is returned by COLUMNS_UPDATED.
You achieve two things with that:
A) you document the expected behavior that your code relies on. That will help future developers that have to maintain your code.
B) You’ll get notified if that behavior changes.
Now, to be clear, there’s no reason to do that with well documented functionality. But if there are documentation gaps, it’s a good practice.

Instead of Triggers - Can they coexist with regular Triggers

Can Instead Of triggers co-exist with regular triggers? If so, are there any potential issues we should be aware of?
INSTEAD OF triggers can coexist with normal triggers. I've done this a good bit.
INSTEAD OF triggers have numerous potential issues, mainly around the fact that what they replace the normal insert/update/delete behavior with whatever you define. A developer may think nothing of UPDATE User SET Address = 'foo' WHERE UserID = 4, but if your trigger is using that as a hook to touch a dozen authentication tables and maybe talk to a server around the world, you've bought yourself a lot of potential confusion.
Keep the behavior of these triggers inline with expected behavior of IUD statements. Don't do too much.
INSTEAD OF triggers are a very powerful tool, easily misused. Use them appropriately and thoughtfully.
I haven't found anything to be concerned about with respect to using both INSTEAD OF and AFTER (AKA FOR) triggers at the same time. The main issues with INSTEAD OF triggers are:
You can only have one INSTEAD OF trigger per operation, per table;
They can mess with OUTPUT INTO clauses (i.e. you'll get identity values of 0);
If you make any schema changes to the table, things may mysteriously break at some point in the future if you weren't careful to maintain the trigger.
None of these caveats are related to AFTER triggers, so you don't really have anything to worry about in that regard. Although I will say that it's more common to write INSTEAD OF triggers on views as opposed to tables, because there's less chance of them interfering with table operations. They were primarily designed as a tool to help you create insertable/updatable views.
Anyway, you'll be fine if you're careful, but I would still recommend against using an INSTEAD OF trigger unless you actually need to, because they make ordinarily simple operations harder to reason about.

What will be the best way to keep track of modified tuples in a database?

I am currently working on a project in which I have to keep track of the tuples that are modified in a relational database. This should include updated tuples, but also inserted and deleted tuples. My question is what will be the best way to accomplish this? I have several ideas of my own, but maybe there are easier/better ways that I did not think of, or there already exists a project that exactly does this.
The final goal of the project is that it will work for relational databases of different vendors, but the first implementation will use a MySQL database. Other database systems can be supported later. But it would be nice if the solution that works for MySQL can be easily adapted to another database.
My first idea was to parse log files. However, I am not certain whether these logfiles contain the actual modified tuples, and furthermore I can imagine that these logfiles will not always be available (e.g. on shared hosting).
My second idea was to intercept the queries at the application level. When a INSERT, DELETE or UPDATE query is performed, these queries can be parsed, and the tuples that they will affect can be determined beforehand. For an INSERT operation this simply is the inserted tuple, and for a DELETE or UPDATE operation the tuples can be identified by applying the WHERE clause in a new SELECT statement.
As a last remark I want to add that performance is not an important factor at this stage of development.
If more details are needed I am happy to provide them.
Use triggers to capture the INSERT, UPDATE, and DELETE and log your entries to a new table. You can use a timestamp on that table to note when the transactions occurred. In the future you can query that table for your modification information.
This will require some database dependent features but you can encapsulate them depending on your architecture but you could use database triggers, which I normally advise against except for this very thing, auditing. In each kind of trigger, you could simply write to a log table whatever info you need. Just one suggestion.

Database design question. BIT column for deletions

Part of my table design is to include a IsDeleted BIT column that is set to 1 whenever a user deletes a record. Therefore all SELECTS are inevitable accompanied by a WHERE IsDeleted = 0 condition.
I read in a previous question (I cannot for the love of God re-find that post and reference it) that this might not be the best design and an 'Audit Trail' table might be better.
How are you guys dealing with this problem?
Update
I'm on SQL Server. Solutions for other DB's are welcome albeit not as useful for me but maybe for other people.
Update2
Just to encapsulate what everyone said so far. There seems to be basically 3 ways to deal with this.
Leave it as it is
Create an audit table to keep track of all the changes
Use of views with WHERE IsDeleted = 0
Therefore all SELECTS are inevitable accompanied by a WHERE IsDeleted = 0 condition.
This is not a really good way to do it, as you probably noticed, it is quite error-prone.
You could create a VIEW which is simply
CREATE VIEW myview AS SELECT * FROM yourtable WHERE NOT deleted;
Then you just use myview instead of mytable and you don't have to think about this damn column in SELECTs.
Or, you could move deleted records to a separate "archive" table, which, depending on the proportion of deleted versus active records, might make your "active" table a lot smaller, better cached in RAM, ie faster.
If you have to have this kind of Deleted Bit column, then you really should consider setting up some VIEWs with the WHERE clause in it, and use those rather than the underlying tables. Much less error prone.
For example, if you have this view:
CREATE VIEW [Current Product List] AS
SELECT ProductID,ProductName
FROM Products
WHERE Discontinued=No
Then someone who wants to see current products can simply write:
SELECT * FROM [Current Product List]
This is much less error prone than writing:
SELECT ProductID,ProductName
FROM Products
WHERE Discontinued=No
As you say, people will forget that WHERE clause, and get confusing and incorrect results.
P.S. the example SQL comes from Microsoft's Northwind database. Normally I would recommend NOT using spaces in column and table names.
We're actively using the "Deleted" column in our enterprise software. It is however a source of constant errors when forgetting to add "WHERE Deleted = 0" to an SQL query.
Not sure what is meant by "Audit Trail". You may wish to have a table to track all deleted records. Or there may be an option of moving the deleted content to paired tables (like Customer_Deleted) to remove the passive content from tables to minimize their size and optimize performance.
A while ago there was some blog uproar on this issue, Ayende and Udi Dahan both posted on this.
Nai this is totally up to you.
Do you need to be able to see who has deleted / modified / inserted what and when? If so, you should design the tables for this and adjust your procs to write these values when they are called.
If you dont need an audit trail, dont waste time with one. Just do as you are with IsDeleted.
Personally, I flag things right now, as an audit trail wasn't specified in my spec, but that said, I don't like to actually delete things. Hence, I chose to flag it. I'm not going to waste a clients time writing something they diddn't request. I wont mess about with other tables because that's another thing for me to think about. I'd just make sure my index's were up to the job.
Ask your manager or client. Plan out how long the audit trail would take so they can cost it and let them make the decision for you ;)
Udi Dahan said this:
Model the task, not the data
Looking back at the story our friend from marketing told us, his intent is to discontinue the product – not to delete it in any technical sense of the word. As such, we probably should provide a more explicit representation of this task in the user interface than just selecting a row in some grid and clicking the ‘delete’ button (and “Are you sure?” isn’t it).
As we broaden our perspective to more parts of the system, we see this same pattern repeating:
Orders aren’t deleted – they’re cancelled. There may also be fees incurred if the order is canceled too late.
Employees aren’t deleted – they’re fired (or possibly retired). A compensation package often needs to be handled.
Jobs aren’t deleted – they’re filled (or their requisition is revoked).
In all cases, the thing we should focus on is the task the user wishes to perform, rather than on the technical action to be performed on one entity or another. In almost all cases, more than one entity needs to be considered.
If you have Oracle DB, then you can use audit trail for auditing. Check the AUDIT VAULT tool form OTN, here. It even supports SQL Server.
Views (or stored procs) to get at the underlying table data are the best way. However, if you have the problem with "too many cooks in the kitchen" like we do (too many people have rights to the data and may just use the table without knowing enough to use the view/proc) you should try using another table.
We have a complete mimic of the base table with a few extra columns for tracking. So Employee table has an EmployeeDeleted table with the same schema but extra columns for when it was deleted and who deleted it and sometimes even the reason for deletion. You can even get fancy and have triggers do the insertion directly instead of going through applications/procs.
Biggest Advantage: no flag to worry about during selects
Biggest Disadvantage: any schema changes to the base table also have to be made on the "deleted" table
Best for: situations where for whatever reason (usually political with us) many not-as-experienced people have rights to the data but still expect it to be accurate without having to understand flags or schemas, etc
I've used soft deletes before on a number of applications I've worked on, and overall it's worked out quite well. Yes, there is the issue of always having to remember to add AND IsActive = 1 to all of your SELECT queries, but really that's not so bad. You can create views if you don't want to have to remember to always do that.
The reason we've done this is because we had very specific business needs to be able to report on records that have been deleted. The reporting needs varied widely - sometimes they'd need to see just the active records, or just the inactive records, or sometimes a mix of both - so pushing all the deleted records into an audit table wasn't a very good option.
So, depending on your particular business needs, I think this approach is certainly a viable option.

Resources