mysql trigger after update column from another table - mysql

I have 2 tables which are below.
I would like to create a trigger for table_one. when changed the username in table_one, update user_id value from the table_two user value.
table_one:
+--------------+----------+
| user_id | username |
+--------------+----------+
| 15 | robin |
| 46 | albert |
+--------------+----------+
table_two:
+--------------+----------+
| id | user |
+--------------+----------+
| 1 | john |
| 2 | jack |
| 3 | robin |
| 4 | kylie |
| 5 | robert |
| 6 | albert |
| 7 | jay |
+--------------+----------+
thanks in advance

Do a BEFORE UPDATE trigger, not an AFTER UPDATE trigger.
(I don't think it's possible in an AFTER UPDATE trigger to modify the row that was updated, that fired the trigger. I could be wrong about that, but I just can't wrap my brain around how that would work.)
This is a demonstration of a BEFORE UPDATE trigger on table_one that assigns a value to the user_id column (of the row being updated) based on the result from a query:
DELIMITER $$
CREATE TRIGGER ...
BEFORE UPDATE ON table_one
BEGIN
# local variable we will temporarily store a value fetched from a query
DECLARE li_new_id BIGINT DEFAULT NULL; # match datatype of table_two.id
# lookup `id` value from `table_two` with a SQL query
SELECT s.id
INTO li_new_id
FROM table_two s
WHERE s.user = NEW.username
ORDER BY s.id
LIMIT 1 ;
# assign the value we fetched to the `user_id` column of the row being updated
SET NEW.user_id := li_new_id ;
END$$
DELIMITER ;
Note that if the query doesn't find a matching row, the local variable will have a NULL value. So the SET statement will assign NULL to to the user_id column. There's no check in the trigger that the value we assign won't violate a constraint (NOT NULL, UNIQUE, FOREIGN KEY).

Related

How to add values to another table's value by MySQL Trigger

I'm a newbie about SQL triggers. I'm trying to create a trigger when a row inserted to actions table, it should get the accounts table's matched account_id and update its value by adding to value.
Accounts table:
+------------------+---------------------+
| Variable_name | Value |
+------------------+---------------------+
| account_id | 1 |
| value | 100 |
+------------------+---------------------+
Inserted row to actions table:
+------------------+---------------------+
| Variable_name | Value |
+------------------+---------------------+
| account_id | 1 |
| value | 10 |
+------------------+---------------------+
I'm trying to make this situation: When inserted to actions with value 10, the row which has 1 account_id in the accounts table, it's value must be 110.
It would be simple update statement like so.
UPDATE accounts SET `value` = `value` + 10 WHERE account_id = 1
How we can do that using trigger. We will pick account_id and value from actions table using NEW keyword and use in this query.
DELIMITER $$
CREATE TRIGGER `actions_after_insert` AFTER INSERT ON `actions` FOR EACH ROW BEGIN
UPDATE accounts
SET `value` = `value` + NEW.`value`
WHERE account_id = NEW.account_id;
END
$$

Database-Trigger to Delete row where condition True:

I need trigger That Deletes row where in table paym both columns table1 and table2 are not empty.
Example in tables below:
table: paym
ID username table1 Table2
+-------+-------------+-------------+-----------+
| 1 | John | Value | Value |
+-------+-------------+-------------+-----------+
| 2 | Alex | Null | Null |
+-------+-------------+-------------+-----------+
Condition is True: After Deleted row:
ID username table1 Table2
+-------+-------------+-------------+-----------+
| 2 | Alex | Null | Null |
+-------+-------------+-------------+-----------+
My attemp is: (Not working)
CREATE trigger DeleteROW
AFTER UPDATE ON paym
FOR EACH ROW
BEGIN
IF (NEW.table1 IS NOT NULL AND NEW.table2 IS NOT NULL) THEN
DELETE
FROM
paym WHERE table1 and table2 IS NOT NULL ;
END IF;
END
A trigger cannot modify the table it is running on.
You should create a stored procedure to handle this, and call that instead of the DELETE command...

Restrict DB table to have only one future dated record

Our project has a requirement that only one future dated record is allowed on a table. Every tables are maintaining record versions using start date and end date. A sample scenario is attached in below screenshot. (assume today's date is 7-Mar-2019)
So, how can I restrict database table to have more than one future dated record. Is there any constraints or triggers would help to do the validation from DB itself ?.( I am using MySQL db)
A simple way might be to overwrite end dates with null in a trigger for example
drop trigger if exists t;
delimiter $$
create trigger t before insert on t
for each row
begin
declare cnt int default 0;
select count(*) into cnt from t where end_date > date(now());
if cnt > 0 then
set new.end_date = null;
end if;
end $$
delimiter ;
insert into t (designation,end_date) values
('a','2019-03-07'),('a','2019-03-07'),('a','2019-04-07'),('a','2019-04-07'),
('b','2019-04-07');
select * from t;
+----+-------------+------------+
| id | designation | end_date |
+----+-------------+------------+
| 1 | a | 2019-03-07 |
| 2 | a | 2019-03-07 |
| 3 | a | 2019-04-07 |
| 4 | a | NULL |
| 5 | b | NULL |
+----+-------------+------------+
5 rows in set (0.00 sec)
You could tweak the code a bit by using an if exists test if you prefer.

SELECT ... FOR UPDATE inside a UPDATE query

I've been trying to implement a simple script that locks a table from being read, updates some fields, and then unlocks it.
Here's my table:
mysql> SHOW COLUMNS FROM tb1;
+---------------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------------+---------+------+-----+---------+-------+
| id | int(11) | YES | PRI | NULL | |
| status | int(1) | YES | | 0 | |
+---------------+---------+------+-----+---------+-------+
Lets see if you guys understand what I'm trying to do:
Start transaction
SELECT all rows with status != 1 (it may return more than 1 row) with FOR UPDATE statement;
UPDATE the field status of the rows that has been selected in pass 2
Commit
I tried to achieve this in many ways, but I cant persist the SELECT data that I got in pass 2 and I can't use SELECT ... FOR UPDATE as a subquery of a UPDATE like this UPDATE tb1 SET status=1 WHERE id IN (SELECT id FROM tb1 WHERE status != 1 FOR UPDATE);
Is it possible to achieve this instead of updating row by row?

Restrict insertion based on a count

So, I need to safely restrict the insertion of entries in a table based on the count of other entries in that same table. Say we have the following table:
resource:(id, foreign_key)
I need to create up to a number of entries based on the foreign key. So, as soon as I reach a count, let's say 100 for our example, I want to restrict creating more entries.
The obvious answer would be something like that:
count the entries with the specified foreign key.
if count < limit insert the new entry
And in fact, that's what I have been using. The thing is, this approach is not fail-proof since between 1 and 2 there might occur another insertion. I considered the possibility of using transactions but (unless I'm completely misunderstanding transactions) this has the same issue:
start transaction
insert the new entry
if entries have exceeded the limit, rollback. otherwise commit
Now, say we already have 99/100 entries and two transactions run at the same time. They both will commit since they don't see each-other's entries.
Short of actually creating the entry and then delete it if it's invalid (which feels kindof messy in my mind) I can't think of a way to solve this issue. Any ideas?
edit: upon request I'm providing sample data:
table1
+-------------+------------------+------+-----+----------------+
| Field | Type | Null | Key | Extra |
+-------------+------------------+------+-----+----------------+
| id | int(10) unsigned | NO | PRI | auto_increment |
| limit | int(10) unsigned | NO | MUL | |
+-------------+------------------+------+-----+----------------+
table2
+-------------+------------------+------+-----+----------------+
| Field | Type | Null | Key | Extra |
+-------------+------------------+------+-----+----------------+
| id | int(10) unsigned | NO | PRI | auto_increment |
| foreign_id | int(10) unsigned | NO | MUL | |
+-------------+------------------+------+-----+----------------+
and some sample data:
table1
+----+----------+
| id | limit |
+----+----------+
| 1 | 5 |
+----+----------+
table2
+----+---------------+
| id | foreign_id |
+----+---------------+
| 1 | 1 |
+----+---------------+
| 2 | 1 |
+----+---------------+
| 3 | 1 |
+----+---------------+
| 4 | 1 |
+----+---------------+
At this point, let's say that two users attempt to create table2 entries. The first one will have to be accepted and the 2nd rejected.
With the first approach, if both users go through step 1 (counting the old entries) and then through step 2 (insert the new entry) both entries will be created.
With the second approach, if both of them run at the same time, they both will count 4 slots before themselves and commit instead of one of them rollbacking.
Halo Mate, a Stored Procedure similar to this structure may help you
UPDATE
DROP PROCEDURE IF EXISTS sp_insert_record;
DELIMITER //
CREATE PROCEDURE sp_insert_record(
IN insert_value1 INT(9),
IN chosen_id INT(9)
)
BEGIN
SELECT id, `limit`
INTO #id, #limit
FROM table1
WHERE id = chosen_id;
START TRANSACTION;
INSERT INTO table2 (id, foreign_id)
VALUES (insert_value1, chosen_id);
SELECT COUNT(id)
INTO #count
FROM table2
WHERE foreign_id = #id;
IF #count <= #limit THEN
COMMIT;
ELSE
ROLLBACK;
END IF;
END//
DELIMITER ;
By using a Stored Procedure, you can also add any validation or process based on your requirements.
Hope this can be of help, cheers!