MYSQL / MariaDB Trigger - After update - mysql

I have 2 similar tables. TableB is containing part of Part A record.
tableA -
+---------+------------+-------+
| id | username | team |
+---------+------------+-------+
| 1 | Peter | W |
+---------+------------+-------+
| 2 | Sam | W |
+---------+------------+-------+
| 3 | Mary | W |
+---------+------------+-------+
tableB -
+---------+------------+-------+
| id | username | team |
+---------+------------+-------+
| 3 | Mary | W |
+---------+------------+-------+
Now I want to update some record in tableB:
UPDATE tableB
SET team = 'K' WHERE username = 'Mary';
How can I use trigger to sync team records in Table A for Mary only?
I am drafting the Trigger like below:
DELIMITER $$
CREATE TRIGGER after_update_tableB
AFTER UPDATE
ON tableB FOR EACH ROW
BEGIN
UPDATE tableA
SET tableA.team = new.team
WHERE tableA.id= new.id;
END $$
DELIMITER ;
I think new.id cannot locate to '3'? Because it hasn't changed?

Related

How do I update a column based on the previous value?

I have read a lot of answers here but I couldn't adapt to my needs.
I have this table below where I would like to update the BALANCE column:
balance = old.balance + new.amount
+----+----------------+---------+------------+-------------+---------------------+------------------+--------+----------+---------+
| ID | TRANSACTION_ID | BANK_ID | ACCOUNT_ID | CUSTOMER_ID | CREATED | DESCRIPTION | AMOUNT | CURRENCY | BALANCE |
+----+----------------+---------+------------+-------------+---------------------+------------------+--------+----------+---------+
| 1 | T1 | 2 | 2 | 1 | 2018-04-22 00:00:00 | TRANSACTION TEST | 100.00 | GBP | NULL |
| 2 | T2 | 2 | 2 | 1 | 2018-04-22 00:00:00 | TRANSACTION TEST | 125.00 | GBP | NULL |
| 3 | T3 | 2 | 2 | 1 | 2018-04-22 00:00:00 | TRANSACTION TEST | -73.00 | GBP | NULL |
+----+----------------+---------+------------+-------------+---------------------+------------------+--------+----------+---------+
This is the result I would like is shown below:
I got it executing:
SET #balance:=0;
UPDATE TRANSACTIONS SET BALANCE = (#balance := #balance + AMOUNT) WHERE ID > 0;
There is no way to fire the statement above after a new column inserted?
+----+----------------+---------+------------+-------------+---------------------+------------------+--------+----------+---------+
| ID | TRANSACTION_ID | BANK_ID | ACCOUNT_ID | CUSTOMER_ID | CREATED | DESCRIPTION | AMOUNT | CURRENCY | BALANCE |
+----+----------------+---------+------------+-------------+---------------------+------------------+--------+----------+---------+
| 1 | T1 | 2 | 2 | 1 | 2018-04-22 00:00:00 | TRANSACTION TEST | 100.00 | GBP | 100.00 |
| 2 | T2 | 2 | 2 | 1 | 2018-04-22 00:00:00 | TRANSACTION TEST | 125.00 | GBP | 225.00 |
| 3 | T3 | 2 | 2 | 1 | 2018-04-22 00:00:00 | TRANSACTION TEST | -73.00 | GBP | 152.00 |
+----+----------------+---------+------------+-------------+---------------------+------------------+--------+----------+---------+
I tried using trigger:
DELIMITER $$
CREATE TRIGGER updateBalance AFTER INSERT ON TRANSACTIONS
FOR EACH ROW
BEGIN
SET NEW.BALANCE = BALANCE + NEW.AMOUNT;
END $$
DELIMITER ;
And I got the error:
Error Code: 1362. Updating of NEW row is not allowed in after trigger
I am new in SQL and MySQL and I believe this is a common task for advanced users.
Values that can be calculated from other (materialized) values shouldn't be materialized as this can lead to inconsistencies.
Remove the column all together.
ALTER TABLE transactions
DROP balance;
And create a view instead:
CREATE VIEW transactions_with_balance
AS
SELECT t1.*,
(SELECT sum(t2.amount)
FROM transactions t2
WHERE t2.bank_id = t1.bank_id
AND t2.account_id = t1.account_id
AND t2.id <= t1.id) balance
FROM transactions t1;
db<>fiddle
If you're using MySQL version 8 or higher you can also replace the subquery by the windowed version of sum()
CREATE VIEW transactions_with_balance
AS
SELECT t1.*,
sum(amount) OVER (PARTITION BY t1.bank_id,
t1.account_id
ORDER BY t1.id) balance
FROM transactions t1;
db<>fiddle
The column customer_id also seems misplaced in the table as I suppose there is an account table where the customer that account belongs to is stored in a foreign key to the customer table. So you can get the customer via the accoount_id.
You can do this with a BEFORE INSERT trigger, summing all the transaction amounts for the given CUSTOMER_ID and adding the new AMOUNT value to get the balance:
DELIMITER $$
CREATE TRIGGER updateBalance BEFORE INSERT ON transactions
FOR EACH ROW
BEGIN
SET NEW.BALANCE = NEW.AMOUNT +
COALESCE((SELECT SUM(AMOUNT)
FROM transactions
WHERE CUSTOMER_ID = NEW.CUSTOMER_ID), 0);
END $$
DELIMITER ;
Demo on dbfiddle
Note you may want to further qualify the sum with
AND BANK_ID = NEW.BANK_ID
and/or
AND ACCOUNT_ID = NEW.ACCOUNT_ID
as necessary to distinguish exactly which records to read the previous transactions from.

mysql insert trigger only if not duplicate

I am little bit confused with the usage of trigger event to get expected result
Here main table is fee. The structure is as follow
Fee
id | rn | fid | amount | f_month | year
====================================================
1 | 1 | 1 | 150000 | 1 | 1
2 | 1 | 2 | 50000 | 1 | 1
3 | 2 | 1 | 550500 | 2 | 1
4 | 2 | 2 | 200 | 2 | 1
5 | 3 | 1 | 550500 | 2 | 1
And the simply insert trigger has been used.
DROP TRIGGER IF EXISTS `insertinv`;
CREATE DEFINER=`root`#`localhost` TRIGGER `insertinv`
AFTER INSERT ON `fee` FOR EACH ROW INSERT INTO invoice VALUES(null, NEW.rn, NEW.year, '')
The output what I am getting
Invoice
inv | rn | y_d | status
==============================
1 | 1 | 1 | 0
2 | 1 | 1 | 0
3 | 2 | 1 | 0
4 | 2 | 1 | 0
5 | 3 | 1 | 0
But I want to apply condition
if fee.rn AND fee.f_month AND fee.year is same then stop to insert. I mean ignore the fee.fid.
and achieve following result. The expected one
Invoice
inv | rn | y_d | status
==============================
1 | 1 | 1 | 0
2 | 2 | 1 | 0
3 | 3 | 1 | 0
In trigger table inv is primary key and auto increment
Check if a inv exists for a matching year and rn.
If it does not exist, then use the insert statement.
Do the following:
DELIMITER $$
DROP TRIGGER IF EXISTS `insertinv` $$
CREATE DEFINER=`root`#`localhost` TRIGGER `insertinv`
AFTER INSERT ON `fee`
FOR EACH ROW
BEGIN
/* Declare a variable to store invoice id for matching year and rn */
DECLARE inv_exists INT(11) DEFAULT 0;
/* Fetch the invoice id if exists */
SELECT inv INTO inv_exists
FROM invoice
WHERE rn = NEW.rn AND
y_d = NEW.year;
/* if no invoice exists then insert into the table */
IF NOT(inv_exists > 0) THEN
/* Insert statement */
INSERT INTO invoice VALUES(null, NEW.rn, NEW.year, '') ;
END IF;
END $$
DELIMITER ;

MYSQL Auto Arrange the id when one row is delete, is it possible?

For example I have a table:
Employee is auto increment
Employee ID | First Name
1 | Chong
2 | Dan
3 | RJ
4 | Joshua
when I delete the row where employee_id = 2, it shows this
Employee ID | First Name
1 | Chong
3 | RJ
4 | Joshua
what I want is to have something like this
Employee ID | First Name
1 | Chong
2 | RJ
3 | Joshua
then when I add another row, for example, I add steve
Employee ID | First Name
1 | Chong
2 | RJ
3 | Joshua
4 | Steve
you can do this using update in the trigger if you want to
SET #count = 0;
UPDATE `tableName` SET `tableName`.`employeeId` = #count:= #count + 1;
and then
ALTER TABLE `tableName` AUTO_INCREMENT = 1;

Update a table in MySQL with all data from another table

I have 3 tables: ak_class, ak_objects, ak_class_object
ak_class:
class_id | class_description | class_name |
1 | some description | some name |
2 | some description | some name |
3 | some description | some name |
ak_objects:
object_id | object_description | object_name |
1 | some description | some name |
2 | some description | some name |
3 | some description | some name |
ak_class_object:
class_object_id | class_id | object_id |
1 | 1 | 1 |
2 | 2 | 2 |
3 | 3 | 3 |
I need to fill in the ak_class_object with a class_id from ak_class table and object_id from ak_objects table.
The question is how can I update (I need to update as there is some wrong data currently) the class_id from the ak_class table with all the ids? I was thinking of using it with JOIN ut I don't know which id to use to Join them as class_id is only to be updated
UPD: I was trying to do it like this, but it didn't work:
DELIMITER $$
DROP PROCEDURE class_object_1$$
CREATE PROCEDURE class_object_1()
BEGIN
DECLARE i INT DEFAULT 0;
WHILE (i < 250000) DO
UPDATE ak_class_object
SET class_id = SELECT DISTINCT class_id from ak_class, object_id = SELECT DISTINCT class_id from ak_objects;
SET i = i + 1;
END WHILE;
END$$
I am writing the generic syntax, change the table names and column name as per your requirements.
update table1 inner join table2
on table1.id = table2.fk_id
set table1.column = table1.columnUpdated

MySQL Trigger to update all entries with a many-many relationship

I am trying to make a MySQL trigger that will update all my users scores if a levels reward is updated. I have got as far as working out the difference between the old and new reward but am stuck on how to update every users score who has completed a level of that. Below is a simplified table structure.
Users
+---------+-------+
| user_id | score |
+---------+-------+
Users_levels
+---------+----------+
| user_id | level_id |
+---------+----------+
Levels
+----------+---------+
| level_id | tier_id |
+----------+---------+
Tier_reward
+---------+----------+
| tier_id | reward |
+---------+----------+
This is how far I have got so far:
CREATE TRIGGER update_level_reward AFTER UPDATE ON tier_reward FOR EACH ROW
BEGIN
DECLARE REWARD INT;
SET REWARD = OLD.reward - NEW.reward;
-- UPDATE users SET score = score - REWARD WHERE user_id = ;
END$$
I have tried something along the lines of:
UPDATE users
INNER JOIN users_levels
ON users.user_id = users_levels.user_id
INNER JOIN levels
ON users_levels.level_id = levels.level_id
SET score = score - REWARD
WHERE levels.tier = OLD.tier;
However this only reduces the score once even if a user has completed more than one level from that tier.
Example
Users
+---------+-------+
| user_id | score |
+---------+-------+
| 1 | 400 |
| 2 | 700 |
+---------+-------+
Users_levels
+---------+----------+
| user_id | level_id |
+---------+----------+
| 1 | 1 |
| 1 | 2 |
| 2 | 1 |
| 2 | 3 |
+---------+----------+
Levels
+----------+---------+
| level_id | tier_id |
+----------+---------+
| 1 | 1 |
| 2 | 1 |
| 3 | 2 |
+----------+---------+
Tier_reward
+---------+----------+
| tier_id | reward |
+---------+----------+
| 1 | 200 |
| 2 | 500 |
+---------+----------+
Now if the reward for tier_id 1 is reduced to 100. User 1 should now have a score of 200 as they have completed two levels of that tier. User 2 should loose only 100 points as they have only completed one level of that tier.
Users
+---------+-------+
| user_id | score |
+---------+-------+
| 1 | 200 |
| 2 | 600 |
+---------+-------+
Tier_reward
+---------+----------+
| tier_id | reward |
+---------+----------+
| 1 | 100 |
| 2 | 500 |
+---------+----------+
Sorry about the delay !!
Here is what you can do, you need to first get the count per tier per user. Then use cursor to set these values and finally update the users table.
delimiter //
create trigger update_level_reward after update on tier_reward
for each row
begin
DECLARE done INT DEFAULT FALSE;
DECLARE tier_count int;
DECLARE user_id_fetch int;
DECLARE reward INT;
DECLARE reward_recal int ;
DECLARE cur CURSOR FOR
select
ul.user_id, count(*) as total
from users_levels ul
join levels l on l.level_id = ul.level_id
where l.tier_id = new.tier_id
group by ul.user_id;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
SET reward = OLD.reward - NEW.reward;
OPEN cur;
update_loop: LOOP
FETCH cur INTO user_id_fetch,tier_count;
IF done THEN
LEAVE update_loop;
END IF;
set reward_recal = tier_count*reward ;
update users
SET score = score - reward_recal
where user_id = user_id_fetch ;
END LOOP;
CLOSE cur;
end ; //
delimiter ;
Here what I did in mysql
create table users (user_id int,score int);
insert into users values (1,400),(2,700);
create table users_levels(user_id int,level_id int);
insert into users_levels values
(1,1),(1,2),(2,1),(2,3);
create table levels (level_id int,tier_id int);
insert into levels values
(1,1),(2,1),(3,2);
create table tier_reward(tier_id int,reward int);
insert into tier_reward values
(1,200),(2,500);
mysql> update tier_reward set reward = 100 where tier_id = 1 ;
Query OK, 1 row affected (0.15 sec)
mysql> select * from users ;
+---------+-------+
| user_id | score |
+---------+-------+
| 1 | 200 |
| 2 | 600 |
+---------+-------+
2 rows in set (0.00 sec)