Trigger update the same table with data from another table after insert - mysql

I have two unrelated tables tbl_A & tbl_B
tbl_A
+----+---------------------+------+
| id | url | slug |
+----+---------------------+------+
| 1 | http://example.com/ | 3qqd |
| 2 | http://example.com/ | t8af |
| 3 | http://example.com/ | sjim |
| 4 | http://example.com/ | awfo |
| 5 | http://example.com/ | 6myy |
+----+---------------------+------+
tbl_A description:
+---------------------+---------------------+------+-----+---------------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------------+---------------------+------+-----+---------------------+----------------+
| id | bigint(20) unsigned | NO | PRI | NULL | auto_increment |
| url | text | YES | | NULL | |
| slug | varchar(255) | YES | MUL | NULL | |
+---------------------+---------------------+------+-----+---------------------+----------------+
and another table :
tbl_B
+----+---------------------+---------------------+------+
| ID | user_name | url | slug |
+----+---------------------+---------------------+------+
| 1 | john.reese | NULL | NULL |
+----+---------------------+---------------------+------+
tbl_B description :
+---------------------+---------------------+------+-----+---------------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------------+---------------------+------+-----+---------------------+----------------+
| ID | bigint(20) unsigned | NO | PRI | NULL | auto_increment |
| user_name | varchar(60) | NO | MUL | | |
| url | text | YES | | NULL | |
| slug | varchar(255) | YES | MUL | NULL | |
+---------------------+---------------------+------+-----+---------------------+----------------+
tbl_A.id is unrelated to tbl_B.ID.
tbl_B.ID is userID field and gets filled up dnamically when a new user registers. So that tbl_B.ID gets a row inserted automatically as a user register, tbl_B.ID value gets auto incremented.
tbl_A on the other hand already exists with all the details.
What I want to achieve: whenever a new user registers and userID is
INSERT into tbl_B.ID, at the same time it should trigger an update of
tbl_B.user and tbl_B.slug with the values taken from tbl_A.user and
tbl_A.slug.
Outcome: After ID 1 is added
+----+---------------------+---------------------+------+
| ID | user_name | url | slug |
+----+---------------------+---------------------+------+
| 1 | john.reese | http://example.com/ | 3qqd |
+----+---------------------+---------------------+------+
Hope I am able to explain. I was trying to use triggers but got lost, am a newbie with mysql, please bear with me.
drop trigger if exists bi_tbl_B $$
delimiter $$
create trigger bi_tbl_B before insert on tbl_B
for each row begin
UPDATE tbl_B
SET url = url +
(SELECT url
FROM tbl_A
WHERE id = NEW.id)
WHERE ID = NEW.ID;
end;
$$
delimiter ;
I don't know whether this is even possible or should I try the other way round.
Add a field user_id in tbl_A and AFTER INSERT on tbl_B update tabl_A.user_id column with the userID from tbl_B.ID
I am open to suggestions, if not trigger then procedures.

It is possible to do it, just not the way you are trying. In the before insert trigger you can change the values being inserted by changing the NEW.field_name variables.
drop trigger if exists bi_tbl_B $$
delimiter $$
create trigger bi_tbl_B before insert on tbl_B
for each row begin
DECLARE v_slug as varchar(255);
DECLARE v_url as text;
SELECT url, slug INTO v_url, v_slug FROM tbl_A WHERE id = NEW.id;
NEW.url=v_url;
NEW.slug=v_slug;
end;
$$
delimiter ;

Related

I want an other table to be updated when I create an entry

I want start_date and start_time copied into latest_time and latest_date, while adding a new entry into my logbook. But I want dependency on logbook.logbook_index_id = logbook_index.id for all entries too.
mysql> describe logbook;
+-------------------------------+-----------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------------------------+-----------------------+------+-----+---------+----------------+
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| logbook_index_id | int(10) unsigned | NO | | NULL | |
| start_date | date | NO | | NULL | |
| start_time | time | NO | | NULL | |
mysql> describe logbook_index;
+--------------------+----------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------------------+----------------------+------+-----+---------+----------------+
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| first_date | date | NO | | NULL | |
| first_time | time | NO | | NULL | |
| latest_date | date | NO | | NULL | |
| latest_time | time | NO | | NULL | |
+--------------------+----------------------+------+-----+---------+----------------+
atm I got this far ...
create trigger update_dates after insert on logbook
for each row update logbook_index
set latest_date = start_date where logbook_index.id = logbook_index_id;
I do it mostly wrong I bet. How does this work correctly and how do I get the time copied too ?
If I understood your question correctly:
For this I would suggest using a trigger
You can put an AFTER INSERT trigger on the table that you insert, inside the trigger you can put the update to the other table.
In order to access variables from the newly insert record, you need to do the following:
UPDATE logbook_index
SET latest_date = NEW.start_date
WHERE logbook_index.id = NEW.logbook_index_id;
Notice the keyword NEW that is used to access the newly insert record.
If you were using an AFTER UPDATE trigger, you could access the old values by using OLD
What you're searching for is a Trigger, a procedure that's automatically invoked in response to an event, in your case the insertion of a row in the logbook table.

Trigger with Ids other than OLD.id

I have a table like this:
user_oauth:
+----------------+-----------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------------+-----------------+------+-----+---------+----------------+
| id | int(8) unsigned | NO | PRI | NULL | auto_increment |
| user_id | int(8) unsigned | NO | MUL | NULL | |
| google_id | varchar(30) | YES | UNI | NULL | |
| facebook_id | varchar(30) | YES | UNI | NULL | |
| windowslive_id | varchar(30) | YES | UNI | NULL | |
+----------------+-----------------+------+-----+---------+----------------+
which contains id of 3 the tables user_facebook, user_google, user_windowslive,
Example for user_facebook:
+-----------+-----------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-----------+-----------------+------+-----+---------+-------+
| id | varchar(30) | NO | PRI | NULL | |
| email | varchar(60) | NO | UNI | NULL | |
| firstname | varchar(30) | YES | | NULL | |
| lastname | varchar(30) | YES | | NULL | |
| link | varchar(100) | YES | | NULL | |
| locale | varchar(5) | YES | | NULL | |
| picture | varchar(200) | YES | | NULL | |
| verified | int(1) unsigned | NO | | NULL | |
+-----------+-----------------+------+-----+---------+-------+
I would like to make a TRIGGER ON DELETE on user_oauth, which will delete user_facebook row if facebook_id of user_oauth is filled.
So I tried:
DELIMITER $$
CREATE TRIGGER `user_oauth_delete` BEFORE DELETE ON `user_oauth`
FOR EACH ROW BEGIN
DELETE FROM user_facebook
WHERE user_facebook.id = user_oauth.facebook_id;
END
$$
DELIMITER ;
But I have the error message:
Unknown column 'user_oauth.facebook_id' in 'where clause'
How to do it?
Thanks.
Solution
I had misunderstanding the OLD statement, so I had to:
DELIMITER $$
CREATE TRIGGER `user_oauth_delete` BEFORE DELETE ON `user_oauth`
FOR EACH ROW BEGIN
DELETE FROM user_facebook
WHERE user_facebook.id = OLD.facebook_id;
END
$$
DELIMITER ;
Don't do this with triggers. Do this with cascading delete constraints.
alter table oath
add constraint fk_oath_facebook
foreign key (facebookid) references user_facebook(id)
on delete cascade;
The documentation does a pretty good job of explaining constraints and the cascading capabilities.
Other than what have been suggested, you can as well consider wrapping both the DML statement inside a transaction block in a stored procedure and pass the facebook_id as parameter to the procedure. Such that both operation will happen in same order. Something like
DELIMITER $$
CREATE PROCEDURE `transaction_sp` (#facebook_id varchar(50))
LANGUAGE SQL
DETERMINISTIC
SQL SECURITY DEFINER
BEGIN
DECLARE exit handler for sqlexception
BEGIN
-- ERROR
ROLLBACK;
END;
DECLARE exit handler for sqlwarning
BEGIN
-- WARNING
ROLLBACK;
END;
START TRANSACTION;
DELETE FROM user_facebook WHERE id = #facebook_id;
DELETE FROM `user_oauth` WHERE facebook_id = #facebook_id;
COMMIT;
END
$$

mysql trigger when no match found

I'm trying to setup a mysql trigger. So lets assume the table is like this
mysql> explain users;
+------------+-------------+------+-----+-------------------+-----------------------------+
| Field | Type | Null | Key | Default | Extra |
+------------+-------------+------+-----+-------------------+-----------------------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(30) | YES | | NULL | |
| age | int(11) | YES | | NULL | |
| purge_date | timestamp | NO | | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |
+------------+-------------+------+-----+-------------------+-----------------------------+
and
mysql> explain users_details;
+-------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(30) | YES | | NULL | |
| age | int(11) | YES | | NULL | |
+-------+-------------+------+-----+---------+----------------+
3 rows in set (0.00 sec)
so the trigger i have setup will create a purge_date of NOW on the users table when something is removed from the users_details table. This is fine.
The issue is that sometimes the data for the users_details table comes through incorrectly and then the users update it so then i need to remove the purge_date if they re-insert the data. Now I have made this trigger
CREATE TRIGGER purge_fix AFTER INSERT ON users_details
FOR EACH ROW
BEGIN
UPDATE users
SET purge_date= '0000-00-00 00:00:00'
WHERE users.name = NEW.name;
END;
Which works fine but in some situations there will not be a link between the users_details table and the users table (it will be created at a later stage) so my question is, as the trigger will fail in some situations (which is 100% fine by me), will I be breaking something having the trigger failing?

mysql after insert trigger which updates another table's column

i'm trying to write a trigger, I have following tables:
BookingRequest:
+-----------+---------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+---------+------+-----+---------+----------------+
| idRequest | int(11) | NO | PRI | NULL | auto_increment |
| roomClass | int(11) | NO | | NULL | |
| inDate | date | NO | | NULL | |
| outDate | date | NO | | NULL | |
| numOfBeds | int(11) | NO | | NULL | |
| status | int(11) | NO | MUL | NULL | |
| idUser | int(11) | NO | MUL | NULL | |
+-----------+---------+------+-----+---------+----------------+
status table:
+------------+--------------------------------------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------------------------------------------+------+-----+---------+-------+
| idStatus | int(11) | NO | PRI | NULL | |
| nameStatus | enum('underConsideration','approved','rejected') | YES | | NULL | |
+------------+--------------------------------------------------+------+-----+---------+-------+
OccupiedRoom:
+--------------+---------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------------+---------+------+-----+---------+----------------+
| idOccupation | int(11) | NO | PRI | NULL | auto_increment |
| idRoom | int(11) | NO | | NULL | |
| idRequest | int(11) | NO | | NULL | |
+--------------+---------+------+-----+---------+----------------+
i need a trigger which will change status in BookingReques to 1 if request with the same id is inserted into OccupiedRoom table, so i tried something like this
create trigger occupy_trig after insert on OccupiedRoom
for each row
begin
if BookingRequest.idRequest= NEW.idRequest
then
update BookingRequest
set status = '1';
where idRequest = NEW.idRequest;
end if;
END;
and it doesn't work, so any suggestions would be very appriciated
Try this:
DELIMITER $$
CREATE TRIGGER occupy_trig
AFTER INSERT ON `OccupiedRoom` FOR EACH ROW
begin
DECLARE id_exists Boolean;
-- Check BookingRequest table
SELECT 1
INTO #id_exists
FROM BookingRequest
WHERE BookingRequest.idRequest= NEW.idRequest;
IF #id_exists = 1
THEN
UPDATE BookingRequest
SET status = '1'
WHERE idRequest = NEW.idRequest;
END IF;
END;
$$
DELIMITER ;
With your requirements you don't need BEGIN END and IF with unnecessary SELECT in your trigger. So you can simplify it to this
CREATE TRIGGER occupy_trig AFTER INSERT ON occupiedroom
FOR EACH ROW
UPDATE BookingRequest
SET status = 1
WHERE idRequest = NEW.idRequest;
Maybe remove the semi-colon after set because now the where statement doesn't belong to the update statement. Also the idRequest could be a problem, better write BookingRequest.idRequest
DELIMITER //
CREATE TRIGGER contacts_after_insert
AFTER INSERT
ON contacts FOR EACH ROW
BEGIN
DECLARE vUser varchar(50);
-- Find username of person performing the INSERT into table
SELECT USER() INTO vUser;
-- Insert record into audit table
INSERT INTO contacts_audit
( contact_id,
deleted_date,
deleted_by)
VALUES
( NEW.contact_id,
SYSDATE(),
vUser );
END; //
DELIMITER ;

MySQL cursor not fetching inside procedure

I have the following tables
albums:
+----------------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------------+-------------+------+-----+---------+----------------+
| album_id | int(11) | NO | PRI | NULL | auto_increment |
| band_id | int(11) | YES | MUL | NULL | |
| release_date | varchar(45) | YES | | NULL | |
| name | varchar(45) | YES | | NULL | |
| format | varchar(45) | YES | | NULL | |
| music_genre_id | int(11) | YES | MUL | NULL | |
| label_id | int(11) | YES | MUL | NULL | |
| avg_rating | float | YES | | NULL | |
+----------------+-------------+------+-----+---------+----------------+
and music_ratings
+-----------------+---------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------------+---------+------+-----+---------+----------------+
| music_rating_id | int(11) | NO | PRI | NULL | auto_increment |
| user_id | int(11) | YES | MUL | NULL | |
| album_id | int(11) | YES | MUL | NULL | |
| rating | int(11) | YES | | NULL | |
+-----------------+---------+------+-----+---------+----------------+
After every insert into the *music_rating* I want to update the average rating in the albums table. I have a trigger for this, which calls a procedure. The thing is, the procedure does not work, for some reason the cursor is not fetching data from the table. (I called the procedure separately to make sure it isn't the trigger acting up. The tables have a couple of rows already, so it's not that.)
My procedure is pretty straight forward and looks like this
DELIMITER $$
CREATE PROCEDURE avg_album_calc(IN id_album INT)
BEGIN
DECLARE done INT DEFAULT 0;
DECLARE rating INT;
DECLARE cur CURSOR FOR SELECT `rating` FROM `music_ratings` WHERE `album_id`=id_album;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
OPEN cur;
SET #ct=0;
SET #sm=0;
REPEAT
FETCH cur INTO rating;
IF NOT done
THEN
SET #ct = #ct +1;
SET #sm = #sm + rating;
END IF;
UNTIL done END REPEAT;
UPDATE albums SET avg_rating = #sm/#ct WHERE album_id = id_album;
CLOSE cur;
END$$
DELIMITER ;
I echoed the result of the cursor with a SELECT rating after the FETCH cur INTO rating; command, and it shows up as null.
You do not need to calculate and store avg_rating in the albums table. You can calculate in on the fly -
SELECT a.album_id, a.name, AVG(mr.rating) FROM albums a
LEFT JOIN music_ratings mr
ON a.album_id = mr.album_id
GROUP BY a.album_id