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

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.

Related

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;

MySQL Trigger not firing AFTER INSERT

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.

ReSet all password status to zero using trigger

I am optimizing my login system in which I am maintain previous passwords of a user, schema of pwd table for maintaining passwords is as follows:
here userId refers to primary key of user table.
If status attribute is 1 that means row has marking current password if it is 0 previous password.
How can I write a trigger so that if a new entry for any user is made in pwd
table, all previous status of that user gets set to 0 and new value will remain 1 as the default value of status attribute. Currently I am doing so at application level.
It won't be possible because it would require to have a mutating (changing the same table it's being fired upon) trigger which is prohibited in MySQL.
You can create a stored procedure though, but you would still need to call it from the client code explicitly.
Now a better schema design would be to keep all current passwords in one table (presumably in the users table) and all previous in the other (i.e. pwd_history). Not only it makes queries for current passwords faster it also allows you to use a trigger if you choose to.
DELIMITER //
CREATE TRIGGER tg_pwd_history
AFTER UPDATE ON users
FOR EACH ROW
BEGIN
IF NOT NEW.pwd <=> OLD.pwd THEN
INSERT INTO pwd_history (`userId`, `pwd`, `ts`)
VALUES (NEW.id, OLD.pwd, NOW());
END IF;
END//
DELIMITER ;
Here is a SQLFiddle demo
Now I very much hope you're not storing them in plain text.

Trigger on Update of a specific field in mySQL

I am trying to replicate the Username field in my database. Specifically I was looking to add/remove the Username across different tables, whenever the row that contains Username is added inside UserDatabase. To that end, I was thinking of using the trigger mechanism.
I am thinking along the lines of:
CREATE TRIGGER 'addUsername' AFTER INSERT ON UserDatabase FOR EACH ROW
IF (UPDATE(Username))
BEGIN
INSERT INTO anothertable (Username) VALUES ('NewUser');
END
My question is that is how do I capture the updated Username from UserDatabase and replicate it into NewUser? And also, is there a way to remove FOR EACH ROW as I only want the loop to run once?
Thanks!
From the manual:
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.
And no, you can not remove for each row and I think you misunderstood it a little. There actually is no loop. for each row refers to multiple rows in your insert or update statement. When you add multiple users with one insert statement, you want all of them replicated, right? Not just one.
Then you should note, that there's a difference between single-quotes and backticks. Backticks should be used, if a column or tablename or whatever includes characters that shouldn't be there, like a space, or if the name is actually a reserved keyword. Single-quotes, like you used them for the trigger name are used to tell MySQL it's a string.
Your trigger should look something like this:
CREATE TRIGGER 'addUsername' AFTER INSERT ON UserDatabase
FOR EACH ROW
INSERT INTO anothertable (Username) VALUES (NEW.Username);
For an update statement, you have to create another trigger.

use trigger with update and insert query both in mysql

i have login page with user name and password, if the user forgot his/her password then there is a link of forgot password,were one has puts its email id and then the default password will send to its id and it will also update it in table with update query,the problem is that when in future we give this project to client we have truncate whole data and give him empty database, at that time update query won't work at that moment i need to use insert query, so i need a trigger query which fires on insert as well as on update query also later on when data is filled, please help because i never used trigger and i don't know its syntax, please explain me by showing an example
You do not need a trigger. Just run an INSERT INTO...ON DUPLICATE KEY UPDATE query. It will insert a row, but if a row with the same primary key already exists, update the column in the existing row instead.
http://dev.mysql.com/doc/refman/5.1/en/insert-on-duplicate.html