Identifying the edited columns in the database - mysql

At the beginning the application have an admins and employees and records.
The records table has many columns than can be changed by any employee. However, this change can not be submitted. When the admin approve for an edit the record will show up again in the system.
I was trying to identify the column name and the value, and send it to another table using triggers on UPDATE.
So when the employee edits any record, the record will be disabled in the system. Also the admin will be able to know which values has been changed.
Is this possible in databases ?
Records Table
-------------------------------------------------------------------
record_id record_name record_serial record_active
-------------------------------------------------------------------
1 something 5151 YES
When an update happens to the record_serial, such as from 5151 to 9844 I need to do this.
Records_changes
-------------------------------------------------------------------
change_id record_col record_old_val record_new_val
-------------------------------------------------------------------
1 record_serial 5151 9844
At the same time
-------------------------------------------------------------------
record_id record_name record_serial record_active
-------------------------------------------------------------------
1 something 9844 NO
I can do it using my application, but if there is anyway to do it using the database it would be much nicer.
I will use this to track the changes, and also create a history for the records old values.
I am using MySQL

You can do something like this
DELIMITER $$
CREATE TRIGGER tg_bu_records
BEFORE UPDATE ON records
FOR EACH ROW
BEGIN
IF NOT (OLD.record_serial <=> NEW.record_serial AND
OLD.record_name <=> NEW.record_name) THEN
SET NEW.record_active = 0;
END IF;
END$$
CREATE TRIGGER tg_au_records
AFTER UPDATE ON records
FOR EACH ROW
BEGIN
IF NOT (OLD.record_serial <=> NEW.record_serial) THEN
INSERT INTO records_changes (record_col, record_old_val, record_new_val)
VALUES ('record_serial', OLD.record_serial, NEW.record_serial);
END IF;
IF NOT (OLD.record_name <=> NEW.record_name) THEN
INSERT INTO records_changes (record_col, record_old_val, record_new_val)
VALUES ('record_name', OLD.record_name, NEW.record_name);
END IF;
END$$
DELIMITER ;
Note: The trick is to change record_active flag in BEFORE trigger because it's the only event when you can change values of a row being updated/inserted in a table on which you defined that trigger. Now in AFTER trigger we record the changes that have been made.
Here is SQLFiddle demo

A good database pattern for this, if I understand your problem correctly, is to use versioned rows. You add columns (or additional referenced tables) with meta data about when/who/what the edit was about. When a record is edited, a new row is inserted. Previous versions are never modified or deleted. Then it's up to your application logic to decide what to do with all of this.

Related

Return only one record (most recent) from a large data set

I want to pull the most recent removed date as one line item.
The below query lists all the names. However, in the output I just want one line item that shows the name with most recent removed date. How do I go about doing this?
select table1.name, table1.removed_date
from table1
where table1.status = 'Removed' and table1.removed_date is not null
order by table1.removed_date desc
Just add limit 1 at the end of your query. Yes, that is the answer.
This may go a bit extreme, but after careful consideration, I suppose there is still a chance that two timestamps could collide even with a precision of milliseconds. Therefore, to define the most recent record, we can take advantage of the latest legit transaction by MySQL. Thus a table recording the latest UPDATE could be very helpful. Fundamentally, the table only needs one line and one column which records the ID of table1(supposing the OP's table1 does have one to avoid duplicate names). Then an UPDATE trigger can do the rest of the job. Here is the code I wrote and tested on workbench:
delimiter //
CREATE TABLE latest_removal (id int(11) )//
insert latest_removal values(null)//
create trigger latest_removed before update on table1 for each row begin
if old.status != new.status and new.status='removed' then
update latest_removal set id=old.id ;
set new.removed_date=current_date(); -- This is optional as people may prefer manual updating.
end if;
end //
Please leave a comment if you find this helpful. Thank you.

PL SQL Trigger to update start time for row when a single column is updated

I'm fairly new to triggers and have already tried searching for a solution to my question with little results. I want to update a single row's start time column whenever it's active column is set to 1.
I have two columns ACTIVE (number) and START_TIME (timestamp) in my_table. I would like to create a PL/SQL trigger that updates the START_TIME column to current_timestamp whenever an update statement has been applied to the ACTIVE column - setting it to 1.
So far I have only seen examples for inserting new rows or updating entire tables which isn't what I'm looking to do. I'd have thought there would be a fairly simple solution to my problem.
This is what I've got so far from other examples. I know the structure of my solution is poor and I'm asking for any input to modify my trigger to achieve my desired result.
CREATE OR REPLACE TRIGGER routine_active
AFTER UPDATE ON my_table
FOR EACH ROW
WHEN (my_table.ACTIVE = 1)
begin
insert my_table.start_time = current_timestamp;
end;
\
you can use like this .it may help you
write the update query instead of insert query
CREATE OR REPLACE TRIGGER routine_active
AFTER UPDATE ON my_table
FOR EACH ROW
WHEN (new.ACTIVE = 1)
begin
update my_table set start_time =current_timestamp;
end;
I think it should be a BEFORE UPDATE, not AFTER UPDATE, so it saves both changes with a single action. Then you don't need the INSERT or UPDATE statements. I also added the "OF active" clause, so it will only start this trigger if that column was updated, which may reduce the workload if other columns get updated.
CREATE OR REPLACE TRIGGER routine_active
BEFORE UPDATE OF active ON my_table
FOR EACH ROW
BEGIN
IF active = 1
THEN
:NEW.start_time = current_timestamp;
END IF;
END;

MySQL trigger: turn UPDATE into UPDATE and INSERT?

I have several tables that I want to enforce versioning, and have an effective from and effective to date. Whenever an application or user writes an UPDATE to this table I want it redirected into two entirely new commands: UPDATE the targeted record so the EFFECTIVE_TO date is populated with current date and time, and INSERT an entirely new record with the updated attributes.
Is this possible to do with a trigger or do I have to keep controlling this externally with a Java application?
Something like this probably
delimiter |
CREATE TRIGGER versioningcontrol AFTER UPDATE ON yourtable
FOR EACH ROW
BEGIN
INSERT INTO yourtable(col1,col2,...) values(...);
END;
|
delimiter ;

Multiple MySQL queries (no PHP)

I am wondering if it is possible to perform a SQL query then update another table with the generated ID and continue through all of the rows?
I have this SQL query that works but what I need to do is after each row is added to cards to then update merged.cars_id with the last generated ID so they are linked. normally I would do this with PHP but ideally I would like to just do it with MySQL if possible.
MAIN QUERY
INSERT INTO cards (first_contact_date, card_type, property_id, user_id)
SELECT first_contact_date, 'P', property_id, user_id FROM merged
THEN I NEED WITH MATCHING ROWS (Roughly)
UPDATE merged SET merged.card_id = LAST_INSERT_ID (FROM ABOVE) into the matching record..
Is something like this possible and how do I do it?
I would recommend using MySQL triggers to do this
http://dev.mysql.com/doc/refman/5.0/en/create-trigger.html
A trigger is a function that will be executed AFTER or BEFORE the INSERT or DELETE or UPDATE is done over any record of your table.
In your case you need to do a AFTER INSERT on cards that just updates the merged table. Make sure its AFTER insert as you wont be able to access the new row's ID otherwise.
The code would look something like this, assuming the id field from the cards table its named "id"
delimiter |
CREATE TRIGGER updating_merged AFTER INSERT ON cards
FOR EACH ROW BEGIN
UPDATE merged SET card_id = NEW.id;
END;
|
delimiter ;
May I suggest Stored Procedures?
http://dev.mysql.com/doc/refman/5.0/en/create-procedure.html
--EDIT--
Ah yes, triggers. For this particular situation, Jimmy has the answer. I will leave this post for the sake of the link.
I would set up a trigger to do this. For mysql, read http://dev.mysql.com/doc/refman/5.0/en/triggers.html. This is what triggers are designed to handle.

How can I use a trigger to update a field in one table, based on data from record insert in another?

I'm new to working with triggers and am having a hard time understanding how to write a trigger to update a field in one table, when a record is inserted in another.
To elaborate, I have 2 tables: servTickets and servTicketNotes.
servTickets has several text fields for customer, contact, phone, email, problem description, status, etc...the PK in this table is an INT field called callID.
servTicketNotes has only 2 fields - again, the PK is an INT field 'callID' and there is a BLOB field called image which stores an image of a service report.
What I'm struggling to do is have a trigger update the status field in servTickets with a value of Closed when a new record is inserted into servTicketNotes.
I'm confused if this is an INSERT AFTER or BEFORE or BOTH scenario, but basically if a report is sent in (thereby creating a record in servTicketNotes, I want the trigger to seek out the record with the same callID in the servTickets table and change the value of status to 'Closed'.
This seems like it should be so simple, but I can't seem to grasp how to get started...
Thanks in advance for your help/guidance!
is it probably a POST trigger - which means:
AFTER you have committed the incoming record, you want to take further action - i.e. inserting into the other table.
if you do it PRE commit, then you would worry about some error happening on the Notes and you might end up with an incorrect update to the status.
You can do this with an AFTER INSERT trigger. Try something like this:
DELIMITER $$
DROP TRIGGER IF EXISTS tr_a_ins_servTicketNotes $$
CREATE TRIGGER pabeta.tr_a_ins_servTicketNotes AFTER INSERT ON servTicketNotes FOR EACH ROW BEGIN
update servTickets
set status = 'Closed'
where callID = NEW.callID;
END $$
DELIMITER ;