simple flow control with mysql - mysql

I've simple example to describe my problem. Lets say I've table structured like this =>
create table A(
id INT(3) NOT NULL AUTO_INCREMENT PRIMARY_KEY,
act DATETIME,
act_reset INT(1) DEFAULT 0);
I want to write query with condition that if act_reset is true (so if not 0) then update column act DATETIME.
I've tried it like this=>
IF act_reset THEN UPDATE A SET act=now() WHERE id=1 END IF;
but this syntax is not valid, how I must write this condition with one query ? thanks

UPDATE A SET act=now() WHERE id=1 AND act_reset <> 0
Is this the query you are looking for?
Using If statement in MySQL :
IF act_reset <> 0 THEN
UPDATE A SET act=now() WHERE id=1
END IF;

You don't need to use the CASE statement or the IF statement in this case. Just move the condition If act_reset to the WHERE clause, like this:
UPDATE A
SET act = now()
WHERE id = 1
AND act_reset <> 0

Assuming act_reset is a variables name. You forgot semicolon, also you need to use DELIMITER
DELIMITER $$
IF act_reset <> 0
THEN
UPDATE A SET act=now()
WHERE id = 1 $$
END IF$$
DELIMITER ;
If act_reset is a column name then in that case it's not possible with IF clause instead try this:
UPDATE A
SET act = now()
WHERE id = 1 AND
act_reset <> 0;

Related

How to check NEW value exists in BEFORE UPDATE Trigger Mysql

I have table Demo:
create table Demo (
id int(10) auto_increment primary key,
text varchar(30) default null,
istrue boolean default false
);
I want create a update trigger such that:
When Update Demo set istrue = false where id = 1; (not "set text = ") -> normal update.
When Update Demo set text= 'abc' where id = 1; ("set text = ") -> SET NEW.text := concat(OLD.text,'#', NEW.text);
I have implemented trigger as follows:
DELIMITER $$
CREATE TRIGGER update_text
BEFORE update ON Demo
FOR EACH ROW
BEGIN
IF (exists(SELECT NEW.text)) THEN -- how to check NEW.text exists???
SET NEW.text := concat(OLD.text,'#', NEW.text);
END IF;
END$$
DELIMITER ;
But it isn't working! Please help. Thank You!!! (My English is not good. Hope everyone sympathized)
CREATE TRIGGER update_text
BEFORE update
ON Demo
FOR EACH ROW
SET NEW.text = CASE WHEN OLD.text <=> NEW.text
THEN NEW.text
ELSE CONCAT(OLD.text, '#', NEW.text)
END;
PS. Single-statement trigger - BEGIN-END and DELIMITER reassing not needed.
After the deep investigation I have found the way to detect that the field value was set in UPDATE statement.
However, I can't guarantee its reliability. I only check does the substring '`text`' is present in current query text - but there is a lot of situations when this check will give errorneous result, for example, wrong TRUE if this substring is a part of new value, or wrong FALSE if the fieldname is not wrapped with backticks. So be aware.
CREATE TRIGGER update_text
BEFORE update
ON demo
FOR EACH ROW
SET NEW.`text` = CASE WHEN LOCATE('`text`', ( SELECT info
FROM information_schema.processlist
WHERE id = CONNECTION_ID()
)
)
THEN CONCAT(OLD.`text`, '#', NEW.`text`)
ELSE NEW.`text`
END;
fiddle

Question about Update on Trigger with condition

I can't find the correct syntax for the trigger I tried to code.
I want to update a timestamp every time there is an update on the table but only for specific rows. I want to update the "UPDATE_TIME" only for the last "N_TACHE"
I can't find how to use SET with a where clause.
CREATE TRIGGER "time_on_update"
BEFORE UPDATE ON "SAVE_UPDATE"
FOR EACH ROW
BEGIN
IF
(NEW.DATE_DE_RDV <> OLD.DATE_DE_RDV)
OR
(NEW.ADRESSE <> OLD.ADRESSE)
OR
(NEW.CODE_POSTAL <> OLD.CODE_POSTAL)
OR
(NEW.VILLE <> OLD.VILLE)
THEN
SET (NEW.UPDATE_TIME = current_timestamp WHERE N_TACHE= (SELECT MAX(N_TACHE));
END IF;
If you have any suggestion I take it.
Thanks in advance
EDIT :
This one is working but it update all the rows and I just want to update "update_time" for the last value of "N_TACHE" when "RDV","ADRESSE","CODE_POSTAL" or "VILLE" is updated
DELIMITER $$
CREATE TRIGGER `time_on_update`
BEFORE UPDATE ON `SAVE_UPDATE`
FOR EACH ROW
BEGIN
IF
(SELECT MAX(N_TACHE)
FROM SAVE_UPDATE
WHERE
((NEW.DATE_DE_RDV <> OLD.DATE_DE_RDV)
OR
(NEW.ADRESSE <> OLD.ADRESSE)
OR
(NEW.CODE_POSTAL <> OLD.CODE_POSTAL)
OR
(NEW.VILLE <> OLD.VILLE)))
THEN
SET NEW.UPDATE_TIME = current_timestamp;
END IF;
END
$$
DELIMITER ;

Use Case statement inside if else block in mysql triggers

I am new to the mysql triggers.
Let assume if i have a table name teachers contains
teacher_id,
teacher_name,
description,
student_id,
class,
student_year
I want to create a AFTER INSERT and AFTER UPDATE triggers on teachers table.
for that i have a table name called teacherslogs with id,teacher_id,student_year,class,student_id columns
so whenever record insert into teachers table it should insert into teacherslogs.(for this trigger able to write)
But for after update trigger facing issue.
Requirement:
whenever student_id changed in teachers table we need to insert a record into teacherslogs with along another columns changes in the teachers table for that record
for this i have written like this
IF ((NEW.student_id <=> OLD.student_id) = 0) Then
select student_id,class,student_year into student_id_,class_,year_ from `employees`.`teachers` where teacher_id = OLD.teacher_id;
Insert into `employees`.`teacherlogs`(teacher_id,student_year,class,student_id) values (OLD.teacher_id,year_,class_,student_id_);
END IF;
But whenever changes in other columns like class,student_year we need to update the teacherslogs table for that record where teacher_id and student_id
Final Trigger Query
create trigger `updateteacherlogs`
AFTER UPDATE ON `employees`.`teacher` FOR EACH ROW
BEGIN
DECLARE teacher_id_ int(11);
Declare student_id_ int(11);
Declare class_ varchar(150);
Declare year_ int(11);
Declare dummy tinyint;
IF ((NEW.student_id <=> OLD.student_id) = 0) Then
select student_id,class,student_year into student_id_,class_,year_ from `employees`.`teachers` where teacher_id = OLD.teacher_id;
Insert into `employees`.`teacherlogs`(teacher_id,student_year,class,student_id) values (OLD.teacher_id,year_,class_,student_id_);
ELSE
CASE WHEN (NEW.student_year <=> OLD.student_year) = 0
THEN update `teacherlogs` set student_year = NEW.student_year where student_id=OLD.student_id and teacher_id = OLD.teacher_id;
WHEN (NEW.class <=> OLD.class) = 0
THEN update `teacherlogs` set class = NEW.class where student_id=OLD.student_id and teacher_id = OLD.teacher_id
ELSE select #dummy END
end if;
END
But i am getting an exception at end if.
is that correct way to solve my problem or any other ways can we do?
The reason for your syntax error is that your CASE statement needs to end in END CASE;, not just END.
I don't think that the CASE statement actually works for your requirements though as if NEW.student_year <=> OLD.student_year then NEW.class will not get compared against OLD.class to see if that value also needs to be updated. That is because (from the manual):
For the second syntax, each WHEN clause search_condition expression is
evaluated until one is true, at which point its corresponding THEN
clause statement_list executes
You can get around that by just replacing the ELSE CASE ... with an ELSEIF clause:
ELSEIF NEW.student_year <=> OLD.student_year OR NEW.class <=> OLD.class THEN
UPDATE teacherlogs
SET student_year = NEW.student_year, class = NEW.class
WHERE student_id = OLD.student_id AND teacher_id = OLD.teacher_id;
This should work because (based on my understanding from your question of how your database is configured) the value of student_year and class in teacherlogs should be the same as OLD.student_year and OLD.class and if they are the same as NEW.student_year and NEW.class the values won't get updated.
If my understanding is incorrect, you will need modify the update to have conditions on each value:
ELSE
UPDATE teacherlogs
SET student_year = IF(NEW.student_year <=> OLD.student_year, NEW.student_year, student_year),
class = IF(NEW.class <=> OLD.class, NEW.class, class)
-- repeat for all columns
WHERE student_id = OLD.student_id AND teacher_id = OLD.teacher_id;
END IF;

How to use Case-When and Update in Trigger?(Error in my code)

I tried to create below Trigger but I have an Error.
Can I Use Update or use 'case when' in trigger?
Please help me to fix my issue here.
Code Explanation:
I want to update my existing row after insert or update.
Do not change the 'FUSDate1' or 'FUSDate2' If I didn't add anything to the 'FUSDate1' or 'FUSDate2'
Update the FUSDate to the New One or Inserted one If I Updated or inserted data to FUSDate
Code:
delimiter //
create trigger SafetyCertificationTRG
after insert on SafetyCertification
for each row
begin
case when (FUSDate1='' or FUZDate1 is NULL) then (FUZDate1=OLD.FUSDate1) else (update SafetyCertification set FUZDate1=NEW.FUSDate1) end;
case when (FUSDate2='' or FUZDate2 is NULL) then (FUZDate2=NEW.FUSDate2) else (update SafetyCertification set FUZDate2=NEW.FUSDate2) end;
end //
delimiter ;
Edit: I am going to add some information here to make the question more clear.
I have one column as FUS that can be get these 3 values only:('FUS1', 'FUS2' and 'FUS3')
I have 3 other columns: FUSDate1, FUSDate2, FUSDate3.
I want to save the Current Date to The FUSDate1 Or FUSDate2 Or FUSDate3 based on the user User Selection of FUS. (They are in same Table)
I used the provided answer and change it to this but I cannot do the above.
Code: This code is just for FUS1 and FUSDate1
delimiter //
CREATE TRIGGER SafetyCertification_bu
BEFORE UPDATE ON SafetyCertification
FOR EACH ROW
BEGIN
-- detect a change made to a value in col
IF OLD.FUS <=> NEW.FUS THEN
-- value of col is not changed, so do nothing
DO 0;
ELSE
-- we detected a new value was assigned to col
IF OLD.FUS ='%FUS1%' THEN
-- we can override the new value, keep it the same
SET NEW.FUSDate1 = CURDATE();
END IF;
END IF;
END //
delimiter ;
Another code that I expected to do my work but still have problem, Does not update Like above Code:
delimiter //
CREATE TRIGGER SafetyCertification_bu
BEFORE INSERT ON SafetyCertification
FOR EACH ROW
BEGIN
IF NEW.FUS='%FUS1%' THEN
SET new.FUSDate1=MD5(CURDATE());
END IF;
END //
delimiter ;
Update 3:
The Third code that provided in answer does not add anything to the FUSdate1 and 2 and 3 when i update or insert any data.
Code
DELIMITER $$
CREATE TRIGGER SafetyCertification_bu
BEFORE UPDATE ON SafetyCertification
FOR EACH ROW
BEGIN
-- set one of the `fusdateN` columns to current date
-- which column to set depends on the value assigned to `fus`
IF NEW.fus = 'FUS1' THEN
SET NEW.fusdate1 = DATE(NOW());
ELSEIF NEW.fus = 'FUS2' THEN
SET NEW.fusdate2 = DATE(NOW());
ELSEIF NEW.fus = 'FUS3' THEN
SET NEW.fusdate3 = DATE(NOW());
END IF;
END$$
DELIMITER ;
A couple of notes:
A trigger cannot issue an UPDATE against the table that fired the trigger, this is prohibited.
The unqualified references (e.g. FUSDate1 and FUZDate1) are not valid. Those references don't resolve to anything.
The reference to OLD.FUSDate1 is invalid in the context of an AFTER INSERT trigger. In an UPDATE trigger, the OLD.col refers to the value of col before the update.
The expression (FUZDate1=OLD.FUSDate1) is not an assignment, it's a comparison that's going to return 0, 1 or NULL.
If we want to trigger on an UPDATE and an INSERT, that will require two separate triggers.
If we want to modify the contents of the current row, we can use BEFORE trigger instead of AFTER.
I want to update my existing row after insert or update.
It would be much easier to apply changes before the row is inserted or updated. We can assign a value to a column col by referencing NEW.col in an assignment in a BEFORE INSERT or BEFORE UPDATE trigger. For example:
SET NEW.col = expr;
Do not change the FUSDate1 or FUSDate2 If I didn't add anything to the FUSDate1 or FUSDate2
This seems pretty straightforward. Just don't make any assignments to NEW.FUSDate1 or NEW.FUSDate2.
Update the FUSDate to the New One or Inserted one If I Updated or inserted data to FUSDate
The example proposed trigger contains references to FUZDate1 or FUZDate2, but there's no mention of these columns in the specification. The specification is confusing.
An UPDATE statement will assign a value to a column, no need for a trigger to do that. An INSERT statement can assign a value to a column, again, no need for a trigger for that.
The specification is not clear. Providing example starting state (rows in the table), and example INSERT or UPDATE statements, and the desired state after the statement is executed would go a long ways towards clarifying the requirements.
A demonstration of a BEFORE UPDATE trigger that prevents a new value being assigned to a particular column:
DELIMITER $$
CREATE TRIGGER SafetyCertification_bu
BEFORE UPDATE ON SafetyCertification
FOR EACH ROW
BEGIN
-- detect a change made to a value in col
IF OLD.col <=> NEW.col THEN
-- value of col is not changed, so do nothing
DO 0;
ELSE
-- we detected a new value was assigned to col
IF OLD.col IS NOT NULL THEN
-- we can override the new value, keep it the same
SET NEW.col = OLD.col;
END IF;
END IF;
END$$
DELIMITER ;
For what this example trigger achieves, we don't necessarily need that many IF conditions; those are included as a demonstrate some of the checks we can perform, how we can make references to the existing value of col and the new value assigned to col.
FOLLOWUP
Based on the update information in the question, here's an example of a BEFORE UPDATE trigger that satisfies the specification.
This is for the UPDATE action. To get this same behavior with an INSERT statement, this trigger definition needs to be repeated, with BEFORE INSERT in place of BEFORE UPDATE.
(I would use the name _bu for the before update trigger, and _bi for before insert trigger.)
DELIMITER $$
CREATE TRIGGER SafetyCertification_bu
BEFORE UPDATE ON SafetyCertification
FOR EACH ROW
BEGIN
-- set one of the `fusdateN` columns to current date
-- which column to set depends on the value assigned to `fus`
IF NEW.fus = 'FUS1' THEN
SET NEW.fusdate1 = DATE(NOW());
ELSEIF NEW.fus = 'FUS2' THEN
SET NEW.fusdate2 = DATE(NOW());
ELSEIF NEW.fus = 'FUS3' THEN
SET NEW.fusdate3 = DATE(NOW());
END IF;
END$$
DELIMITER ;
second followup
To answer the question about using CASE WHEN statement in the context of a MySQL stored program...
We could implement the example trigger (directly above in this answer) replacing the IF-THEM with either of the two forms of the CASE statement.
either
CASE
WHEN NEW.fus = 'FUS1' THEN
SET NEW.fusdate1 = DATE(NOW());
WHEN NEW.fus = 'FUS2' THEN
SET NEW.fusdate2 = DATE(NOW());
WHEN NEW.fus = 'FUS3' THEN
SET NEW.fusdate3 = DATE(NOW());
END CASE;
-or-
CASE NEW.fus
WHEN 'FUS1' THEN
SET NEW.fusdate1 = DATE(NOW());
WHEN 'FUS2' THEN
SET NEW.fusdate2 = DATE(NOW());
WHEN 'FUS3' THEN
SET NEW.fusdate3 = DATE(NOW());
END CASE;
note
The CASE statement is available in MySQL stored programs; outside of a stored program, it is not a valid SQL statement.
Also, we should not confuse this CASE statement with the CASE expression. The CASE expression is valid in the context of a SQL statement, such as a SELECT or UPDATE statement.
DEMONSTRATION
"I examined the Trigger, But unfortunately does not save anything in fusDates."
#Christiano: here is a simple demonstration
create table
CREATE TABLE `safety_certification`
( id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY
, fus VARCHAR(5)
, fusdate1 DATE
, fusdate2 DATE
, fusdate3 DATE
) ENGINE=INNODB
;
populate table with demonstration rows
INSERT INTO `safety_certification` (id, fus, fusdate1, fusdate2, fusdate3) VALUES
( 1, '', NULL, NULL, NULL)
,( 2, '', NULL, NULL, NULL)
,( 3, '', NULL, NULL, NULL)
,( 4, '', NULL, NULL, NULL)
,( 5, '', NULL, NULL, NULL)
;
create trigger
DELIMITER $$
CREATE TRIGGER `safety_certification_bu`
BEFORE UPDATE ON `safety_certification`
FOR EACH ROW
BEGIN
-- set one of the `fusdateN` columns to current date
-- which column to set depends on the value assigned to `fus`
IF NEW.fus = 'FUS1' THEN
SET NEW.fusdate1 = DATE(NOW());
ELSEIF NEW.fus = 'FUS2' THEN
SET NEW.fusdate2 = DATE(NOW());
ELSEIF NEW.fus = 'FUS3' THEN
SET NEW.fusdate3 = DATE(NOW());
END IF;
END$$
DELIMITER ;
updates will exercise BEFORE UPDATE trigger we just defined
UPDATE `safety_certification` sc SET sc.fus = 'FUS1' WHERE sc.id = 1 ;
UPDATE `safety_certification` sc SET sc.fus = 'FUS2' WHERE sc.id = 2 ;
UPDATE `safety_certification` sc SET sc.fus = 'FUS3' WHERE sc.id = 3 ;
UPDATE `safety_certification` sc SET sc.fus = 'FUS4' WHERE sc.id = 4 ;
display contents of table
SELECT * FROM `safety_certification`;
returns:
id fus fusdate1 fusdate2 fusdate3
------ ------ ---------- ---------- ------------
1 FUS1 2018-05-04 (NULL) (NULL)
2 FUS2 (NULL) 2018-05-04 (NULL)
3 FUS3 (NULL) (NULL) 2018-05-04
4 FUS4 (NULL) (NULL) (NULL)
5 (NULL) (NULL) (NULL)
seems like the trigger is populating the columns fusdate1, fusdate2 and fusdate3 per the specification, when some particular values are assigned to fus
I had a same problem and I used two trigger,
First trigger send data to the other table, so now you can create another trigger to send this data to the first table. I think it is low performance but it is the only way that I reached to it. I hope others have another solution for u.

Update multiple tables with single query, if a condition is met?

How do I update 2 tables in single UPDATE SET, under some condition ?
If the condition is not met, I'd only update one table..
I'd like to do something like this:
UPDATE tab1, tab2 SET
tab1.value2=7,
CASE tab1.value1 IS NOT NULL
WHEN true
THEN tab2.value1 = tab1.value1
END
WHERE tab1.id=1 AND tab2.id = tab1.tab2_fk_id
MySQL workbench complains: Syntax Error: Unexpected CASE_SYM
I think I should do this with a TRIGGER - function
Here how you can do it,
update tab1 ,tab2
join tab2 on tab2.id = tab1.tab2_fk_id
set
tab1.value2=7,
tab2.value1 = case when tab1.value1 IS NOT NULL then tab1.value1 else tab2.value1 end
where tab1.id=1
Many thanx to Abhik Chakraborty for a brillant solution. It will be surely useful in other context. I have though decided to create a trigger in static_conveyor (~ tab1 in the above example) to achieve equal behavior. I use real tables and column names here instead of generic.. :
USE `tca`;
DELIMITER $$
CREATE DEFINER=`root`#`localhost` TRIGGER `static_conveyor_AUPD` AFTER UPDATE ON `static_conveyor` FOR EACH ROW
BEGIN
IF (OLD.signal != NEW.signal)
THEN
IF ((NEW.pallet_id IS NOT NULL) AND (NEW.default_empty IS NOT NULL))
THEN
UPDATE pallet SET empty = NEW.default_empty WHERE pallet.id = NEW.pallet_id;
END IF;
END IF;
END
Works nice ..