Get column name and values for MySQL trigger - mysql

I'm beginner with MySQL and I'm trying to create a log trigger that fills a log table like this:
DELIMITER $$
CREATE TRIGGER ai_user AFTER UPDATE ON user
FOR EACH ROW
BEGIN
INSERT INTO user_log (action,id,timestamp,column_name,old_value, new_value)
VALUES('update',NEW.id,NOW(),COLUMN.NAME,OLD.column_value, NEW.column_value);
END$$
DELIMITER ;
But I'm with problems to get the changed column name and it's old and new value.
Any help would be appreciated. Thanks.

You have to do this the painful way, one at a time:
if not (old.col1 = new.col1 or old.col1 is null and new.col1 is null)
INSERT INTO user_log(action, id, timestamp, column_name, old_value, new_value)
VALUES('update', NEW.id, NOW(), 'col1', OLD.col1, NEW.col1);
end if;
if not (old.col2 = new.col2 or old.col2 is null and new.col2 is null)
INSERT INTO user_log(action, id, timestamp, column_name, old_value, new_value)
VALUES('update', NEW.id, NOW(), 'col2', OLD.col2, NEW.col2);
end if;
. . .
Note that these do not have else clauses. You want to check for every value.
By the way, when you do updates this way, you need to be careful about types if the columns you are comparing have different types.

Related

Increment a number field via a trigger INSERT in MySQL

I'm building a data versioning system, and I need to increment a version number each time a new row is added to the version table, but it increments once and then stops:
DELIMITER |
CREATE TRIGGER trigger2 AFTER UPDATE ON something
FOR EACH ROW
BEGIN
IF NEW.updated_at <> OLD.updated_at THEN
INSERT INTO versions_something (
`id`,
`some_id`,
`version`,
`title`,
`description`,
`created_at`,
`updated_at`
) VALUES (
null,
NEW.id,
1,
NEW.title,
NEW.description,
NOW(),
NOW()
);
END IF;
UPDATE
versions_something
SET
version = (SELECT MAX(version)) + 1
WHERE versions_something.id = LAST_INSERT_ID();
END;
|
DELIMITER ;
I've tried putting the UPDATE into a separate trigger (AFTER INSERT ON versions_something ...), but MySQL complains that it's clashing with the trigger before it.
I've tried the UPDATE on its own, using the last ID in the table and it works each time, so I have no idea what's happening.

"after update" trigger fires after insert in mySQL

I have 2 triggers on my table as defined...
DELIMITER $$
CRATE TRIGGER newRecordToHistory AFTER INSERT ON myTable FOR EACH ROW
BEGIN
IF (new.recordType = 1) THEN
INSERT INTO myTableHistory
(
myTableId,
someInformation,
reason,
mytimestamp,
status
)
VALUES
(
new.myTableId,
new.someInformation,
new.reason,
now(),
'NEW'
);
END IF;
END$$
DELIMITER ;
and
DELIMITER $$
CRATE TRIGGER updateRecordToHistory AFTER UPDATE ON myTable FOR EACH ROW
BEGIN
IF (new.recordType = 1) THEN
INSERT INTO myTableHistory
(
myTableId,
someinformation,
reason,
mytimestamp,
status
)
VALUES
(
new.myTableId,
new.someInformation,
new.reason,
now(),
'UPDATED'
);
END IF;
END$$
DELIMITER ;
When I insert a new record into myTable, I get 2 records in myHistoryTable...
ID someInformation reason mytimestamp status
1 'This is new' 'Needed new record' 09/12/14 08:00:00 'NEW'
1 'This is new' 'Needed new record' 09/12/14 08:00:00 'UPDATED'
I also get a record in the table when I delete. But my code handles inserting into the history table when I delete (so the user can specify the reason for deleting it) from myTable.
I would only expect only one record in myHistoryTable on insert and no extra records in the myHistoryTtable when I delete (other than the ones I put there manually). Why does this happen? And how can I avoid this?
Thanks!

Mysql triggers - capture each column change

I am trying to create trigger, that capture changes in database after update.
Table my_table I am watching:
Table my_table_log where I am writing changes to log them
And here is trigger so far:
CREATE TRIGGER `log_update`
AFTER UPDATE ON `my_table`
FOR EACH ROW
BEGIN
INSERT INTO
`my_table_log`
(
`id`,
`action`,
`column_name`,
`value_before`,
`value_after`,
`who`,
`ts`
)
VALUES
(
NEW.id,
'u',
'name',
OLD.name,
NEW.name,
user(),
NOW()
);
END
Question: How to log each change of column ?
Problem: I am curently watching only if column name changed in my_table. And I have another trigger for column age. How to set trigger for each row and each column that was changed?
Thank you for your suggestions/code/inspirations
You might use ifs for every column you'd like to watch in your trigger:
create trigger `log_update`
after update on `my_table`
for each row
begin
if (old.name <> new.name) then
insert into `my_table_log`
(
`id`,
`action`,
`column_name`,
`value_before`,
`value_after`,
`who`,
`ts`
)
values
(
new.id,
'u',
'name',
old.name,
new.name,
user(),
now()
);
end if;
if (old.age <> new.age) then
insert into `my_table_log`
(
`id`,
`action`,
`column_name`,
`value_before`,
`value_after`,
`who`,
`ts`
)
values
(
new.id,
'u',
'age',
old.age,
old.age,
user(),
now()
);
end if;
end
But better make the insert a stored procedure to avoid redudancy:
create procedure `log_insert`
(
id int(11),
`action` char,
column_name varchar(255),
value_before varchar(255),
value_after varchar(255)
)
begin
insert into `my_table_log`
(
`id`,
`action`,
`column_name`,
`value_before`,
`value_after`,
`who`,
`ts`
)
values
(
id,
`action`,
column_name,
value_before,
value_after,
user(),
now()
);
end
And call it in your trigger:
create trigger `log_update`
after update on `my_table`
for each row
begin
if (old.name <> new.name) then
call log_insert
(
new.id,
'u',
'name',
old.name,
new.name
);
end if;
if (old.age <> new.age) then
call log_insert
(
new.id,
'u',
'age',
old.age,
new.age
);
end if;
end
You can re-use the stored procedure to log events in your insert and delete triggers.
Make shure to use a composite primary key in your my_table_log to allow updates over several columns. I'd use at least:
primary key(id,column_name,who,ts).
Or use dedicated single column primary key to avoid varchars in your primary key for better performance.
One alternative is to just log the new values together with user() and now():
create table my_table_log
( id ...
, name ...
, age ...
, action ...
, who ...
, ts ... )
To determine what was changed, compare with the previous row.
It is however rather expensive to determine what a row looked like at a certain point in time, you will have to find the last version before that point in time. Another model that makes this a lot easier is to keep track of begin_ts and end_ts for each row:
create table my_table_log
( id ...
, name ...
, age ...
, action ...
, who ...
, begin_ts ...
, end_ts ...)
The insert trigger adds a copy of the row with begin_ts = now() and end_ts = null. The update trigger updates end_ts = now() where end_ts is null and inserts a row like the insert trigger. The delete trigger updates end_ts and might add a copy together with who deleted the row. Determining what a row looked like at ts t is just a matter of where t between start_ts and end_ts

MYSQL Insert values from another table - trigger

I have these tables in my database:
I want to add the registod and alarmes table one idRegisto .
The alarm table is populated automatically by a trigger. I would like to connect the two tables and the table alarmes populated idRegistos automatically by a trigger with the values โ€‹โ€‹of table records.
Does anyone can help me please.
I hope I have explained well my doubts
Thank you
My Trigger that populated table alarmes
DELIMITER $$
create TRIGGER alerta
BEFORE INSERT ON registos
FOR EACH ROW
begin
Set #comp=0;
Set #tempmax=0;
Set #tempmin=0;
Set #hummax=0;
Set #hummin=0;
Set #orvalho=0;
select lim_inf_temp, lim_sup_temp, lim_sup_humid, lim_inf_humid, lim_pt_orvalho into #tempmin, #tempmax, #hummax, #hummin, #orvalho from sensores where idSensor=NEW.idSensor;
Set #maxidAlarme=0;
if (CAST(NEW.Temperatura AS UNSIGNED)<#tempmin) then
SELECT MAX(idAlarme) into #maxidAlarme FROM alarmes;
SET #maxidAlarme=#maxidAlarme+1;
INSERT INTO alarmes(idAlarme,descricao_alarme) VALUES (#maxidAlarme,"ERROR");
end if;
end $$;
DELIMITER ;
In alarm table, do you want to use the same newly generated idRegisto of registos table? - Ravinder
Yes. This is what i want. โ€“ user3320956
To insert the same newly generated idRegisto field value in alarm table,
Change part of your trigger body as below:
if ( CAST( NEW.Temperatura AS UNSIGNED ) < #tempmin ) then
SELECT MAX( idAlarme ) into #maxidAlarme FROM alarmes;
SET #maxidAlarme := #maxidAlarme + 1;
SET #auto_idRegisto := ( SELECT AUTO_INCREMENT
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME = 'registos'
AND TABLE_SCHEMA = DATABASE() );
INSERT INTO alarmes( idAlarme, descricao_alarme, idRegisto )
VALUES ( #maxidAlarme, "ERROR", #auto_idRegisto );
end if;

Multiple If conditions in a trigger

i want to write multiple if conditions to check the values of different columns of a same table in a trigger. i am checking for the two columns right now and i am getting the mysql error
#1064(syntax error) at Line # 17.
following are my conditions. plz help me what im doing wrong.
IF (OLD.CE_EN_OPTION_ID != NEW.CE_EN_OPTION_ID)
THEN
INSERT INTO audit_log(
BENEFICIARY_ID ,
TABLE_NAME ,
FIELD_NAME ,
OLD_VALUE ,
NEW_VALUE ,
EDIT_BY,
DATE_TIME
)
VALUES (
OLD.BENEFICIARY_ID, 'be_ce_main', 'CE_EN_OPTION_ID', OLD.CE_EN_OPTION_ID, NEW.CE_EN_OPTION_ID, NEW.EDITED_ID,NOW()
);
END IF;
IF(OLD.CE_DM_OPTION_ID != NEW.CE_DM_OPTION_ID)
THEN
INSERT INTO audit_log(
BENEFICIARY_ID ,
TABLE_NAME ,
FIELD_NAME ,
OLD_VALUE ,
NEW_VALUE ,
EDIT_BY,
DATE_TIME
)
VALUES (
OLD.BENEFICIARY_ID, 'be_ce_main', 'CE_DM_OPTION_ID', OLD.CE_DM_OPTION_ID, NEW.CE_DM_OPTION_ID, NEW.EDITED_ID,NOW()
);
END IF;
You are missing BEGIN - END block.
And I fear you did not override delimiter to instruct sql engine to not execute statements ending with default statement terminator ; semicolon. Because you didn't define new delimiter, sql engine assumed it was the end of statement at first found ; semicolon. There it failed because the statements are not in proper syntax order.
Try the following:
Delimiter //
CREATE TRIGGER 'test_trigger' AFTER UPDATE ON 'be_ce_main' FOR EACH ROW
BEGIN -- this is a must if you have more than one executable statements below
-- your trigger body here
END;
//
Delimiter ;
Refer to:
MySQL: Create Trigger Syntax