How to avoid recurive trigger calls with INSERT trigger also calling INSERT? - mysql

I'm new to triggers and all of these procedures stuff. I'm trying to create a simple AFTER INSERT trigger which will duplicate a friendship but swap user_id's, so both users have one. Here's the structure:
And here's my trigger:
DELIMITER $$
CREATE
/*[DEFINER = { user | CURRENT_USER }]*/
TRIGGER `combat`.`friendship_insert_trigger` AFTER INSERT
ON `combat`.`friendships`
FOR EACH ROW BEGIN
DECLARE userID INT;
DECLARE friendID INT;
SET userID = NEW.friend_id;
SET friendID = NEW.user_id;
INSERT INTO friendships VALUES(userID, friendID, NEW.status);
END$$
DELIMITER ;
As you can see, it calls INSERT with swapped values.
Is there any way to 'tell' the new insert statement to not call the trigger again?
I'm using SQLyogg and Ubuntu 16 Mysql
An example:
I insert a friendship with user_id = 1, friend_id = 2 and status = 0.
It should also insert a friendship with user_id = 2, friend_id = 1 and status = 0;

Related

Before Insert Trigger to check a value exists in database

I would like to creating a trigger that updates the database when a new message is sent.
If the SenderId and RecipentId already have a conversationId, then update the database so that the New.conversationId = existing conversationId.
Table: mailbox_message
Create a trigger that updates the conversationId to the same value whenever users 11 & 5 send messages to each other.
delimiter $$
create trigger before_insert_conversationId
before insert on data25k29_mailbox_message
for each row
begin
IF ( EXISTS (
SELECT data25k29_mailbox_message.conversationId FROM data25k29_mailbox_message
WHERE (data25k29_mailbox_message.senderId='5'
and data25k29_mailbox_message.recipientId = '11')
and (data25k29_mailbox_message.senderId= '11'
and data25k29_mailbox_message.recipientId= '5')
)
)
THEN
SET NEW.conversationId = data25k29_mailbox_message.conversationId;
ELSE
SET NEW.conversationId = NEW.conversationId;
END IF;
end
$$
The value doesn't change after inserting values. Any help in this regard is greatly appreciated. Thanks
This is my working snippet of this question.
Drop trigger if exists after_insert_conversationId;
delimiter |
CREATE TRIGGER after_insert_conversationId
before insert ON data25k29_mailbox_message
FOR EACH ROW
BEGIN
Declare v1 int;
select conversationId into v1 from data25k29_mailbox_message
where senderId = new.senderId and recipientId = new.recipientId limit 1;
if v1 is NULL
then
set new.conversationId = new.conversationId;
else
set new.conversationId = v1;
End if;
END|

Mysql multiple table sync

I have two tables from Xenforo:
xf_user
xf_user_authenticate
Table xf_user stores all the info of a user except password hashes, which are stored in xf_user_authenticate.
Both tables have the same column called user_id.
When data is inserted into xf_user_authenticate I need to get the user_id from the new inserted row, then use that user_id to get the username from xf_user and set its value to xf_user_authenticate.
I tried this code, but it doesn't work:
CREATE TRIGGER name_sync AFTER INSERT ON xf_user_authenticate
begin
SELECT 'username' INTO #username FROM xenforo.xf_user WHERE 'user_id'=NEW.user_id;
UPDATE xenforo.xf_user_authenticate SET 'username' = #username;
end
You have few syntax related issues, in addition you are trying to update the same table where you have after insert trigger. You may need to change the trigger to before insert as
delimiter //
create trigger name_sync before insert on xf_user_authenticate
for each row
begin
declare user_name_sel varchar(100);
select user_name into user_name_sel
from xf_user
where user_id = NEW.user_id;
set new.username = user_name_sel;
end;//
delimiter ;
CREATE TRIGGER name_sync BEFORE INSERT ON xf_user_authenticate
BEGIN
DECLARE cUsername VARCHAR(255);
SELECT username INTO cUsername FROM xenforo.xf_user WHERE user_id=NEW.user_id;
SET NEW.username = cUsername;
END

INSERT INTO if conditions are met

A user is only meant to have up to 3 keys registered to his account at any one time. To add a new key, the user must first delete another key to "make room" for a new one.
I want this to be checked server-side, but I can't get the query to work. Here is what I tried:
IF (SELECT COUNT(serial_key_nbr)
FROM keys_table WHERE user_id = 9) <= 2
THEN INSERT INTO keys_table (user_id, serial_key_nbr)
VALUES (9, 'abc123')
How to do this?
You can use the below mention Script for the same:
INSERT INTO keys_table (user_id, serial_key_nbr)
SELECT 9, 'abc123' FROM DUAL
WHERE
(SELECT COUNT(serial_key_nbr)
FROM keys_table WHERE user_id = 9)<=2
if you want to use an if to do a conditional select then I would put it in a variable like so.
BEGIN
DECLARE var1 INT;
SELECT COUNT(serial_key_nbr) INTO var1
FROM keys_table
WHERE user_id = 9;
IF var1 <= 2
THEN
INSERT INTO keys_table (user_id, serial_key_nbr)
VALUES (9, 'abc123')
END IF;
A trigger might be the way to go. If a condition is met, a trigger before inserting in the table can perform an invalid operation and cause the insert operation to fail:
delimiter $$
create trigger keep_three before insert on keys_table for each row
begin
if (select count(serial_key_nbr) from keys_table where user_id = new.user_id) >= 3 then
insert into non_existent_table (non_existent_field) values (new.user_id);
end if;
end$$
delimiter ;
Ugly, but it might work.
Reference:
"MySQL Triggers: How do you abort an INSERT, UPDATE or DELETE with a trigger?"
Another solution (better I think) is to forcibly delete an entry before attepting the insert. When there are less than 3 entries, the insert procedes normally:
delimiter $$
create trigger keep_three before insert on keys_table for each row
begin
while (select count(serial_key_nbr) from keys_table where user_id = new.user_id) >= 3 do
delete from keys_table where user_id = new.user_id
-- OPTIONAL: Add an ordering criteria to define which entry is deleted first
limit 1;
end while;
end$$
delimiter ;
I think this is cleaner.
A third way (I've found it here). It will return an error message (by signaling sqlstate 45000: Unhandled user defined exception) associated with the defined condition:
delimiter $$
create trigger keep_three before insert on keys table for each row
begin
declare msg varchar(255);
declare n int default 0;
set n = (select count(serial_key_nbr) from keys_table where user_id = new.user_id);
if n >= 3 then
set msg = "INSERT failed: There must be only three entries for each user. Delete an entry first";
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = msg;
end if;
end$$
delimiter ;
A cleaner version of my first option.

MySQL Trigger Insert After with Select query from different table

new to DBA thanks for bearing with me.
Overview:
I have Groups, Subgroups and Users.
User can be owner of Group so should be Owner of all its subgroups
User can be collaborator or follower of group so should be collaborator or follower of all its subgroups
User can be collaborator or follower of just subgroup
Tables are as follow (simplified):
Group(topic_id,title)
Subgroup (subtopic_id,title,topic_id)
rel_Group (user_id,topic_id,type)
//To Determine relationship of user to Group (Owner,Collaborator or Follower)
rel_Subgroup (user_id,subtopic_id,type)
//To Determine relationship of user to Subgroup (Owner,Collaborator or Follower)
User (user_id)
I want to create a trigger when a subgroup is created that will INSERT / UPDATE / DELETE rows in rel_Subgroup so users who are Owner, Collaborator or follower on group with respectively be Owner, Collaborator or follower on subgroup
This is the closest i got but am still getting:
#1415 - Not allowed to return a result set from a trigger.
SQL Query
delimiter //
create trigger Transfer_Rights_to_Subgroup
after insert
on Subgroup
for each row
begin
select user_id,type from rel_Group where rel_Group.topic_id = NEW.topic_id;
insert into rel_Subgroup VALUES (rel_Group.user_id,NEW.subtopic_id,rel_Group.type);
END; //
delimiter ;
I am hoping to sort the insert and then will figure out the update/delete.
Any help, much appreciated!
thx
Managed to solve it:
DROP TRIGGER IF EXISTS Transfer_Rights_to_Subgroup;
DELIMITER //
CREATE TRIGGER Transfer_Rights_to_Subgroup AFTER INSERT ON subgroup
FOR EACH ROW
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE c1 INT;
DECLARE c2 INT;
DECLARE cur CURSOR FOR SELECT User_ID,Type FROM rel_group WHERE rel_group.Topic_ID = NEW.Topic_ID;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN cur;
ins_loop: LOOP
FETCH cur INTO c1,c2;
IF done THEN
LEAVE ins_loop;
END IF;
INSERT INTO rel_Subgroup VALUES (c1,NEW.Subtopic_ID,c2);
END LOOP;
CLOSE cur;
END; //
DELIMITER ;
Try this one:
delimiter //
create trigger Transfer_Rights_to_Subgroup
after insert
on Subgroup
for each row
begin
select user_id,type into #userid, #type from group where rel_Group.topic_id = NEW.topic_id;
insert into rel_Subgroup VALUES (#userid,NEW.subtopic_id,#type);
END; //
delimiter ;

Trigger not working as expected

I trying execute an trigger on mysql database.
The command executes successfully, but the trigger not working.
DELIMITER #
CREATE TRIGGER generate_coupon AFTER INSERT ON order
FOR EACH ROW
BEGIN
DECLARE userid, couponvalue INT;
DECLARE couponcode VARCHAR;
SELECT idUser INTO userid FROM indication WHERE email = NEW.email;
SET couponvalue = 20;
SET couponcode = 'abc123';
INSERT INTO coupon(idUser,idOrder,couponvalue,couponcode) values(userid, NEW.id, couponvalue, couponcode);
END#
DELIMITER ;
I suspect your problem arises from the collisions between your variables couponvalue and couponcode with the same-named columns in your coupon table. As documented under Local Variable Scope and Resolution:
A local variable should not have the same name as a table column.
You could simplify your trigger to the following and, in so doing, avoid this problem entirely:
CREATE TRIGGER generate_coupon AFTER INSERT ON order FOR EACH ROW
INSERT INTO coupon
(idUser, idOrder, couponvalue, couponcode)
SELECT idUser, NEW.id, 20, 'abc123'
FROM indication
WHERE email = NEW.email
;