Trigger not working as expected - mysql

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
;

Related

MySQL stored procedure - How to get last insert id

I have created one stored procedure which inserts a record into table and gets auto incremented ID of that record. Here I am getting an syntax error while setting LAST_INSERT_ID() into a variable.
ERROR 1064 (42000): You have an error in your SQL syntax; check the
manual that corresponds to your MySQL server version for the right
syntax to use near ');
SET _orderId = SELECT LAST_INSERT_ID(); END' at line 5
Please help me to solve this issue. Thanks in Advance.
My code is like below:
DELIMITER //
CREATE PROCEDURE placeOrder(IN _cartId INT, IN _createdBy INT)
BEGIN
DECLARE _orderId INT;
-- insert into order
INSERT INTO `TBL_ORDER`(`DealerId`, `OrderNo`, `CreatedBy`)
VALUES ((SELECT DealerId FROM TBL_SHOPPING_CART WHERE Id = _cartId), UNIX_TIMESTAMP(), _createdBy));
SET _orderId = SELECT LAST_INSERT_ID();
END//
DELIMITER ;
Try this.
delimiter //
CREATE PROCEDURE placeOrder(IN _cartId INT,IN _createdBy INT)
BEGIN
SET #orderId = '';
-- insert into order
INSERT INTO `TBL_ORDER`(`DealerId`, `OrderNo`, `CreatedBy`) VALUES ((SELECT DealerId FROM TBL_SHOPPING_CART WHERE Id = _cartId),UNIX_TIMESTAMP(),_createdBy));
SELECT LAST_INSERT_ID() INTO #orderId;
END//
delimiter ;
OR
delimiter //
CREATE PROCEDURE placeOrder(IN _cartId INT,IN _createdBy INT)
BEGIN
-- insert into order
INSERT INTO `TBL_ORDER`(`DealerId`, `OrderNo`, `CreatedBy`) VALUES ((SELECT DealerId FROM TBL_SHOPPING_CART WHERE Id = _cartId),UNIX_TIMESTAMP(),_createdBy);
SELECT LAST_INSERT_ID() AS '_orderId ';
END//
delimiter ;
You should make sure that your application is not having a global connection or shared connection. As last_insert_it() will return the last generated AI value it can be from any table. Especially if your host application is using async TASKs
Consider following scenario
Your application saves gps location every second => generating new AI value
You're trying above SP to insert a value => Generating new AI value
Between your insert and read last_insert_id, your application logs gps location again and created new AI value.
Now guess what happens? you get the last inserted id from the gps table not from your SP.
usually insert's are fast, but assume your SP had to wait for a table lock and got delayed. In such case, you will receive wrong ID.
Safest way can be to escapsulate your SP work within a transaction and get the max value of your AI column (assuming it's an unsigned AI column).
Try:
...
-- SET _orderId = SELECT LAST_INSERT_ID();
SET _orderId = LAST_INSERT_ID();
SELECT _orderId;
...
or
...
-- SET _orderId = SELECT LAST_INSERT_ID();
SET _orderId = (SELECT LAST_INSERT_ID());
SELECT _orderId;
...

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 issue on insert

I'm running an insert into a members table and when a new row is added I want to run a trigger to update the username field of the members table but it wont let me due to constraints due to possible deadlock situations.
DELIMITER //
CREATE TRIGGER tr_add_member
AFTER INSERT ON td_members
FOR EACH ROW BEGIN
IF mem_username = '' THEN
SET mem_username = CONCAT('user' , mem_id);
END IF;
END//
DELIMITER ;
I've tried using the OLD and NEW keywords but they don't work, I've removed the NEW and OLD keywords above but get the below error with this trigger.
ERROR 1193 (HY000): Unknown system variable 'mem_username'
Should I be calling a procedure from the trigger to do what I want it and just run a simple UPDATE statement from within the procedure?
You have to use BEFORE INSERT trigger, but not an AFTER INSERT.
And if mem_id is auto incremented primary key field, then find its
next auto increment value from information_schema.tables and use it.
Change your trigger code as follows:
DELIMITER //
DROP TRIGGER IF EXISTS tr_add_member //
CREATE TRIGGER tr_add_member
BEFORE INSERT ON td_members
FOR EACH ROW
BEGIN
DECLARE _mem_id INT DEFAULT 0;
IF length( trim( NEW.mem_username ) ) = 0 THEN
SELECT AUTO_INCREMENT INTO _mem_id
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME = 'td_members'
AND TABLE_SCHEMA = DATABASE();
SET NEW.mem_username = CONCAT( 'user', _mem_id );
END IF;
END;
//
DELIMITER ;

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 ;