Reputation: 13223
This is a very common thing in web applications. If I have a user table and I want to keep track of all the changes made to the user table, I can use a database insert and update triggers to save those changes in the user_history table.
But what if I have user_products table, where I have user_id , product_id, cost. When I add a user in the system lets say I have two products associated with that user. So my user_products table will have two rows for that user.
user_id product_id cost
1 10 1000
2 20 2000
Now if I go to the edit user page and delete product 1 , add product 3 , change cost for product 2 from 2000 to 3000.
so normally I delete all records for user_id 1 from user_product table and then do a new insert for the new products.
So its not a regular update but a delete and then insert. Hence I am not able to keep track of history changes.
Ideally I would want to know that I deleted product 1 , added product 3 , changed cost for product 2 from 2000 to 3000.
EDIT 1:-
I am not doing a update. I am doing a delete and then insert. So I am deleting record with product id 2 and cost 2000. And then again inserting record with prod id 2 but with cost 3000. So technically its delete and insert but logically only cost is changed from 2000 to 3000. If i check while executing both queries it will say i deleted product with id 2 and and then added product with id 2 which are same. But I want to be able to see that the cost has chnaged from 2000 to 3000
Upvotes: 4
Views: 3518
Reputation: 231851
One option would be to create a user_product_history
table that is populated by triggers on user_product
and then define a trigger that transforms the old 'delete' row in the history table into an update
if the row is subsequently inserted.
CREATE TABLE user_product_history (
user_id number,
product_id number,
cost number,
operation_type varchar2(1),
operation_date date
);
CREATE TRIGGER trg_user_product_history
AFTER INSERT OR UPDATE OR DELETE ON user_product
FOR EACH ROW
DECLARE
l_cnt integer;
BEGIN
IF( deleting )
THEN
insert into user_product_history( user_id, product_id, cost, operation_type, operation_date )
values( :old.user_id, :old.product_id, :old.cost, 'D', sysdate );
ELSIF( updating )
THEN
insert into user_product_history( user_id, product_id, cost, operation_type, operation_date )
values( :new.user_id, :new.product_id, :new.cost, 'U', sysdate );
ELSIF( inserting )
THEN
select count(*)
into l_cnt
from user_product_history
where operation_type = 'D'
and user_id = :new.user_id
and product_id = :new.product_id;
if( l_cnt > 0 )
then
update user_product_history
set operation_type = 'U',
operation_date = sysdate,
cost = :new.cost
where operation_type = 'D'
and user_id = :new.user_id
and product_id = :new.product_id;
else
insert into user_product_history( user_id, product_id, cost, operation_type, operation_date )
values( :new.user_id, :new.product_id, :new.cost, 'I', sysdate );
end if;
END IF;
END;
From an efficiency standpoint, however, doing deletes and inserts rather than updates is going to mean that you're putting far more load on your database than is necessary. You'll do substantially more I/O than necessary. And you'll end up with much more complicated code for handling changes. You'll almost certainly be better served figuring out what has changed and then just updating those rows.
Upvotes: 4
Reputation: 310
I don't know if there is a "standard" way or simple one but for my experience, you have to do it yourself... Of course, you don't need to code a specific method for each table but a generic method that handles all update SQL, something smart according your tables structure. Example: You can define a history table that will have: id - table name - table record id - list of fields to update - list of values updated BEFORE - list of values updated AFTER
Then, into your code, you should have an abstract class that will handle the SQL queries: on update, you call the method that will parse the SQL query and insert a record into this table, even you have to query a SELECT to retrieve the data BEFORE the updates (the overhead is minimum because the SELECT uses only few resources and you can use the DB server cache). Finally, when you display the information relative to a record, let's say the user, you can add the code to display the history on this table AND for this user id.
Hope it will help you.
Upvotes: 1