MySQL Trigger after update only if row has changed
As a workaround, you could use the timestamp (old and new) for checking though, that one is not updated when there are no changes to the row. (Possibly that is the source for confusion? Because that one is also called 'on update' but is not executed when no change occurs)
Changes within one second will then not execute that part of the trigger, but in some cases that could be fine (like when you have an application that rejects fast changes anyway.)
For example, rather than
IF NEW.a <> OLD.a or NEW.b <> OLD.b /* etc, all the way to NEW.z <> OLD.z */
THEN
INSERT INTO bar (a, b) VALUES(NEW.a, NEW.b) ;
END IF
you could use
IF NEW.ts <> OLD.ts
THEN
INSERT INTO bar (a, b) VALUES(NEW.a, NEW.b) ;
END IF
Then you don't have to change your trigger every time you update the scheme (the issue you mentioned in the question.)
EDIT: Added full example
create table foo (a INT, b INT, ts TIMESTAMP);
create table bar (a INT, b INT);
INSERT INTO foo (a,b) VALUES(1,1);
INSERT INTO foo (a,b) VALUES(2,2);
INSERT INTO foo (a,b) VALUES(3,3);
DELIMITER ///
CREATE TRIGGER ins_sum AFTER UPDATE ON foo
FOR EACH ROW
BEGIN
IF NEW.ts <> OLD.ts THEN
INSERT INTO bar (a, b) VALUES(NEW.a, NEW.b);
END IF;
END;
///
DELIMITER ;
select * from foo;
+------+------+---------------------+
| a | b | ts |
+------+------+---------------------+
| 1 | 1 | 2011-06-14 09:29:46 |
| 2 | 2 | 2011-06-14 09:29:46 |
| 3 | 3 | 2011-06-14 09:29:46 |
+------+------+---------------------+
3 rows in set (0.00 sec)
-- UPDATE without change
UPDATE foo SET b = 3 WHERE a = 3;
Query OK, 0 rows affected (0.00 sec)
Rows matched: 1 Changed: 0 Warnings: 0
-- the timestamo didnt change
select * from foo WHERE a = 3;
+------+------+---------------------+
| a | b | ts |
+------+------+---------------------+
| 3 | 3 | 2011-06-14 09:29:46 |
+------+------+---------------------+
1 rows in set (0.00 sec)
-- the trigger didn't run
select * from bar;
Empty set (0.00 sec)
-- UPDATE with change
UPDATE foo SET b = 4 WHERE a=3;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
-- the timestamp changed
select * from foo;
+------+------+---------------------+
| a | b | ts |
+------+------+---------------------+
| 1 | 1 | 2011-06-14 09:29:46 |
| 2 | 2 | 2011-06-14 09:29:46 |
| 3 | 4 | 2011-06-14 09:34:59 |
+------+------+---------------------+
3 rows in set (0.00 sec)
-- and the trigger ran
select * from bar;
+------+------+---------------------+
| a | b | ts |
+------+------+---------------------+
| 3 | 4 | 2011-06-14 09:34:59 |
+------+------+---------------------+
1 row in set (0.00 sec)
It is working because of mysql's behavior on handling timestamps.
The time stamp is only updated if a change occured in the updates.
Documentation is here:
https://dev.mysql.com/doc/refman/5.7/en/timestamp-initialization.html
desc foo;
+-------+-----------+------+-----+-------------------+-----------------------------+
| Field | Type | Null | Key | Default | Extra |
+-------+-----------+------+-----+-------------------+-----------------------------+
| a | int(11) | YES | | NULL | |
| b | int(11) | YES | | NULL | |
| ts | timestamp | NO | | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |
+-------+-----------+------+-----+-------------------+-----------------------------+
MySQL trigger with conditions on UPDATE
Try something like this:
CREATE TRIGGER Increment
AFTER UPDATE ON Subscription
FOR EACH ROW
BEGIN
IF NEW.State <> OLD.State AND NEW.STATE='C' THEN
UPDATE USER
SET num_sub=num_sub+1
WHERE USER.id = NEW.user_id; -- change this condition as you need
END IF;
END
This should also work:
CREATE TRIGGER Increment
AFTER UPDATE ON Subscription
FOR EACH ROW
UPDATE USER
SET num_sub=num_sub+1
WHERE USER.id = NEW.user_id
AND NEW.State <> OLD.State
AND NEW.STATE='C';
Note: NEW
and OLD
refer to the row from the Subscription
table which is being updated. OLD
contains the values before the update. NEW
contains the values after the update. So to check if the state
has been changed you can compare NEW.State <> OLD.State
.
Trigger for rows where specific column was changed
You need to compare the inserted
and deleted
tables, joining by primary key, and verify the results are different.
Note that the UPDATE()
function only tells you if the column was present in the UPDATE
, not if the value actually changed.
CREATE TRIGGER testTrigger
ON table
AFTER UPDATE
AS
SET NOCOUNT ON;
IF(UPDATE(KeyProperty))
BEGIN
UPDATE t
SET Flag = True
FROM (
SELECT i.Id, i.KeyProperty
FROM inserted i
EXCEPT
SELECT d.Id, d.KeyProperty
FROM deleted d
) i
JOIN table t ON t.Id = i.Id
END
GO
Find which columns have changed in MySQL triggers
It would be best to use a CDC tool like Debezium for this instead of triggers.
You can get a list of columns belonging to your Entity
table by querying INFORMATION_SCHEMA.COLUMNS
, but that doesn't help. It's not possible to generate dynamic statements in a trigger based on the columns, because MySQL doesn't support running dynamic SQL in a trigger.
If you must use triggers, then you just have to hard-code all 40 columns.
You could streamline it a little bit, by using the new feature in MySQL 8.0 feature to add a WHERE clause to a SELECT without a table reference. If the WHERE clause is false, the SELECT returns zero rows. Then you can string these together with UNION to make a set of rows, one for each column that changed.
BEGIN
INSERT INTO ENTITY_AUD (baseTableId, beforeValue, afterValue, changedBy)
SELECT OLD.id, OLD.quantity, NEW.quantity, NEW.updated_by
WHERE OLD.quantity <> NEW.quantity
UNION
SELECT OLD.id, OLD.price, NEW.price, NEW.updated_by
WHERE OLD.price <> NEW.price
UNION
SELECT OLD.id, OLD.billing, NEW.billing, NEW.updated_by
WHERE OLD.billing <> NEW.billing
UNION
...37 others...
END
This question has come up numerous times in the past. Here are a few I found, but undoubtedly there are more.
- mysql Trigger for logging, find changed columns (2012)
- MySQL update trigger - find changed columns? (2013)
- Mysql triggers - capture each column change (2014)
- MYSQL update Trigger check changes in all columns and insert values to other table (2019)
- MySQL Trigger compare all columns in OLD and NEW (2021)
mysql trigger when update or insert - update field in affected row/rows
When you want to change a column of the row being inserted/updated, don't use UPDATE
, just use SET NEW.status = ...
That will change the value only on the current row that spawned the trigger.
Also you must qualify the other columns as NEW.stock
and NEW.delivery_days
.
BEGIN
IF NEW.stock != OLD.stock or NEW.delivery_days != OLD.delivery_days
THEN
SET NEW.status = CheckStatus(NEW.stock, NEW.delivery_days);
END IF;
END
I agree with the comment above that you shouldn't name your table new
, or any other SQL keyword or reserved word, because it will make your code confusing to anyone who reads it.
MySQL After Update TRIGGER with IF Conditional Statement
You don't need a query to get the status, just use new.status
in the IF
statement.
DELIMITER $$
CREATE TRIGGER book_returned AFTER UPDATE ON borrowed_books
FOR EACH ROW
IF new.status = 'returned'
THEN
UPDATE books b
SET b.availableCopies=b.availableCopies + New.copies
WHERE b.book_ID=new.book_ID;
END IF;
$$
DELIMITER ;
Create mysql trigger to update specific column after update row
Your trigger should be :
DELIMITER $$
CREATE TRIGGER after_user_update BEFORE UPDATE ON group_chat_message
FOR EACH ROW
BEGIN
SET NEW.`server_modified_at` = CONCAT(SUBSTRING(REPLACE(NOW(), '-', ''),1,8),SUBSTRING(REPLACE(NOW(), ':', ''),12,8));
END;$$
DELIMITER ;
So basically, you should trigger it BEFORE and not AFTER, and you don't need to create this UPDATE instruction in the trigger. Instead, just modify the NEW value of server_modified_at
before updating the row
Note that, if the aim is to store the timestamp when a record is modified, you can create a column like this in your table :
server_modified_at TIMESTAMP NOT NULL ON UPDATE CURRENT_TIMESTAMP
and MySQL will set the current timestamp to your server_modified_at column automatically when the row is updated, you don't need a trigger for that.
Related Topics
How to Select Rows With Max(Column Value), Partition by Another Column in MySQL
Why Is Select * Considered Harmful
How to Use Group by to Concatenate Strings in SQL Server
Are Postgresql Column Names Case-Sensitive
SQL Query Return Data from Multiple Tables
SQL Split Values to Multiple Rows
Insert into ... Values ( Select ... from ... )
SQL Join and Different Types of Joins
Get Records With Max Value For Each Group of Grouped SQL Results
Select Top 10 Records For Each Category
How to Speed Up Insertion Performance in Postgresql
MySQL - How to Unpivot Columns to Rows
Null in MySQL (Performance & Storage)