MySQL Trigger not firing AFTER INSERT - mysql

I have created a new trigger in my MySQL database to update a cell value after an insert. The cell that I want to update (gp_per) as default is set to a null value.
Here is the trigger:
SET #gp_per = ((#retail_price - (#retail_price * 0.23)) - #cost_price) / (#retail_price * 0.23)
It created fine and I got no syntax errors but when I try to insert a new row, the gp_per cell is down as NULL rather than updating with the correct value. I haven't really used triggers before so I'm not 100% sure what I am doing wrong.
Below is the trigger:
And the structure of the table:
Thanks for any feedback!

As MySQL documentation on creating triggers says:
Within the trigger body, you can refer to columns in the subject table (the table associated with the trigger) by using the aliases OLD and NEW. OLD.col_name refers to a column of an existing row before it is updated or deleted. NEW.col_name refers to the column of a new row to be inserted or an existing row after it is updated.
Still MySQL documentation on trigger examples says:
In a BEFORE trigger, you can also change its value with SET NEW.col_name = value if you have the UPDATE privilege for it. This means you can use a trigger to modify the values to be inserted into a new row or used to update a row. (Such a SET statement has no effect in an AFTER trigger because the row change will have already occurred.)
The above means that you need to use
SET NEW.gp_per=((NEW.retail_price - (NEW.retail_price * 0.23)) - NEW.cost_price) / (NEW.retail_price * 0.23)
expression and you need to change your trigger to be a before insert trigger. You can also consider using a generated column instead of a trigger, if your MySQL version supports it.

Related

Using values from an insert in a trigger

I'm using a trigger that triggers AFTER INSERT, when the user inserts data into the the table i want to be able to use the information that was just inserted into the table in my trigger.
Is there any way to do this?
Use the special variable new to get the data for the new added row(s), as described in the manual at 13.1.22 CREATE TRIGGER Statement
Within the trigger body, you can refer to columns in the subject table (the table associated with the trigger) by using the aliases OLD and NEW. OLD.col_name refers to a column of an existing row before it is updated or deleted. NEW.col_name refers to the column of a new row to be inserted or an existing row after it is updated.

MySQL: How to pass parameter to a trigger

I have a table on a mysql 5.7 db, containing say athletes with their mean, max, avg times in a specific sport. I have another table that lists some calculated statistics based on those values.
I managed to do the calculcations that end up on the second using stored procedures. I use as input parameter to the stored procedure the athlete's name.
So when in the first table, an athlete is inserted (with his/her avg/min/max times) or his/her values are updated and I run the stored procedure, the later updates the statistics table.
My question is how to achieve the same result with triggers?
I guess it is feasible/easy to update the entire table on each insert or update of the first table. What would be more efficient performance-wise, would be on each :
INSERT into table1 values (..) where athlete_name="John Do"
(...)
ON DUPLICATE KEY UPDATE (...)
Run a trigger in the pseudocode form :
INSERT into statistics_table values (..) where athlete_name="John Do"
ON DUPLICATE KEY UPDATE (...)
How can the the athlete_name="John Do" be passed to the trigger dynamically, to avoid update the entire statistics table?
You cannot pass any parameters to a trigger and the insert statement does not support the where clause either.
Having said this, a trigger can pick up the user's name from the record being inserted / updated / deleted using NEW.athlete_name or OLD.athlete_name (whichever is required) and use that to call a stored procedure:
Within the trigger body, the OLD and NEW keywords enable you to access
columns in the rows affected by a trigger. OLD and NEW are MySQL
extensions to triggers; they are not case-sensitive.
In an INSERT trigger, only NEW.col_name can be used; there is no old
row. In a DELETE trigger, only OLD.col_name can be used; there is no
new row. In an UPDATE trigger, you can use OLD.col_name to refer to
the columns of a row before it is updated and NEW.col_name to refer to
the columns of the row after it is updated.
A column named with OLD is read only. You can refer to it (if you have
the SELECT privilege), but not modify it. You can refer to a column
named with NEW if you have the SELECT privilege for it. In a BEFORE
trigger, you can also change its value with SET NEW.col_name = value
if you have the UPDATE privilege for it. This means you can use a
trigger to modify the values to be inserted into a new row or used to
update a row. (Such a SET statement has no effect in an AFTER trigger
because the row change will have already occurred.)
You can create triggers that fire after each insert or update on the parent table (athletes). Within each trigger, you can access the value of column athlete_name on the record that was just created or changed, and then invoke your stored procedure using CALL().
Here is a code sample for such an INSERT trigger :
CREATE TRIGGER athletes_upd AFTER INSERT ON athletes
FOR EACH ROW
BEGIN
CALL my_procedure(NEW.athlete_name);
END;
UPDATE trigger :
CREATE TRIGGER athletes_upd AFTER UPDATE ON athletes
FOR EACH ROW
BEGIN
CALL my_procedure(NEW.athlete_name); -- or maybe OLD.athlete_name ?
END;

UPDATE Same Row After UPDATE in Trigger

I want the epc column to always be earnings/clicks. I am using an AFTER UPDATE trigger to accomplish this. So if I were to add 100 clicks to this table, I would want the EPC to update automatically.
I am trying this:
CREATE TRIGGER `records_integrity` AFTER UPDATE ON `records` FOR EACH ROW SET
NEW.epc=IFNULL(earnings/clicks,0);
And getting this error:
MySQL said: #1362 - Updating of NEW row is not allowed in after trigger
I tried using OLD as well but also got an error. I could do BEFORE but then if I added 100 clicks it would use the previous # clicks for the trigger (right?)
What should I do to accomplish this?
EDIT - An example of a query that would be run on this:
UPDATE records SET clicks=clicks+100
//EPC should update automatically
You can't update rows in the table in an after update trigger.
Perhaps you want something like this:
CREATE TRIGGER `records_integrity` BEFORE UPDATE
ON `records`
FOR EACH ROW
SET NEW.epc=IFNULL(new.earnings/new.clicks, 0);
EDIT:
Inside a trigger, you have have access to OLD and NEW. OLD are the old values in the record and NEW are the new values. In a before trigger, the NEW values are what get written to the table, so you can modify them. In an after trigger, the NEW values have already been written, so they cannot be modified. I think the MySQL documentation explains this pretty well.
Perhaps you could write two separate statements in that transaction
update record set clicks=...
update record set epc=...
or you could put them inside a function, say updateClick() and just call that function. By doing it this way you can easily alter your logic should the need arise.
Putting the logic inside a trigger might create a situation where debugging and tracing are made unnecessarily complex.

Trigger on a field is fired unexpectedly when I update another field

Here is my table:
Id Password Status
1 a6cc890.. 1
I have a trigger upon Password which is used to encrypt the field.
The trigger is as below:
-- Trigger DDL Statements
DELIMITER $$
USE `ediftpdb`$$
CREATE
DEFINER=`edidbo`#`%`
TRIGGER `ediftpdb`.`trigger_format_passwd`
BEFORE INSERT ON `ediftpdb`.`users`
FOR EACH ROW
SET NEW.passwd=md5(NEW.passwd)$$
CREATE
DEFINER=`edidbo`#`%`
TRIGGER `ediftpdb`.`trigger_format_passwd_update`
BEFORE UPDATE ON `ediftpdb`.`users`
FOR EACH ROW
SET NEW.passwd=md5(NEW.passwd)$$
To my surprise, the trigger is fired when I update Status, and the password is encrypted again!
What should I do to resolve this issue?
If you update Status, you're doing an UPDATE and all of the UPDATE triggers will fire. All you need to do is compare the new and old values of passwd and only apply your MD5 if they are different. Lucky for you, MySQL supplies OLD and NEW row aliases:
You can refer to columns in the subject table (the table associated with the trigger) by using the aliases OLD and NEW. OLD.col_name refers to a column of an existing row before it is updated or deleted. NEW.col_name refers to the column of a new row to be inserted or an existing row after it is updated. Try using IF:
BEFORE UPDATE ON `ediftpdb`.`users`
FOR EACH ROW
SET NEW.passwd = IF(NEW.passwd = OLD.passwd, NEW.passwd, md5(NEW.passwd))
This shouldn't change NEW.passwd unless NEW.passwd (the new password) and OLD.passwd (the hashed password that is already in the database) are different. Of course, this might fail if someone manages to enter the MD5 of their old password as their new password but that's pretty unlikely.
When you "update Status", you're actually performing an UPDATE on the whole table. The fact that you only modify the value of one field doesn't come into it!
You can manually check whether the value of passwd has changed using the NEW and OLD identifiers.

How to change a column value in AFTER INSERT trigger?

I have a trigger AFTER INSERT inside which I must update the value of a column of the row that has been just inserted. I obviously cannot use :
SET new.column = value;
I've tried to do a manual update in the trigger but it's also not allowed.
Is there any simple way to work this out ?
Many thanks !
An AFTER trigger is typically used to update something other than the row being updated. For example, if you wanted to log the fact that an update had been made, an AFTER trigger is ideal.
To change the value of a column as it is being inserted, you need to use a before trigger. For example
CREATE TRIGGER modify_column BEFORE INSERT ON mytable
SET #column = value;
Where value is a query, pre defined value or NEW.column
Since the INSERT is already done by the time the AFTER is fired, I think you will have to write T-SQL to change the value based on your primary key.
If you want to change it before it gets inserted, you may want to consider moving to a BEFORE trigger instead.