MySQL trigger: On UPDATE, DELETE old row if exists and cancel update - mysql

I have a table in which I want to perform an UPDATE. My problem is that the update changes the primary keys and may produce an error because of the collision, eg: I want to update keys to go from 1-15 to 1-16, and 1-16 exists, so error arises
I know how to do it via PHP, but I only need UPDATE if there is no collision, and DELETE the old row and cancel UPDATE if a collision may occur. So I decided to use a trigger because I see it really clean, eg: if I want to UPDATE 1-15 to 1-16, and 1-16 exists, delete 1-15 and abort UPDATE. This is the trigger I'm trying to create:
CREATE TRIGGER table_trigger BEFORE UPDATE ON table
FOR EACH ROW
BEGIN
IF ( SELECT COUNT(1) FROM table WHERE item_id = NEW.item_id AND related_id = NEW.related_id ) THEN
DELETE FROM table WHERE item_id = OLD.item_id AND related_id = OLD.related_id;
ROLLBACK TRANSACTION;
END IF;
END;//
But is giving back an MySQL error:
SQL Error (1064): 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 '' at line 5
which is the DELETE line. I have not so much knowledge about triggers, how can I achieve it what I'm trying? Am I on the right way?

OP's comment:
I use ROLLBACK TRANSACTION to try to abort the update
As per documentation on Triggers (even functions) can't use explicit commit/rollback. It is not permitted.
The trigger cannot use statements that explicitly or implicitly begin or end a transaction, such as START TRANSACTION, COMMIT, or ROLLBACK.
If you don't want to update not be executed on a condition matching, you better throw an error. It won't let the update happen.
Example:
delimiter //
drop trigger if exists table_trigger //
CREATE TRIGGER table_trigger BEFORE UPDATE ON table
FOR EACH ROW
BEGIN
declare rowCount int default 0;
declare error_message varchar(1024) default '';
SELECT COUNT(1) into rowCount FROM table
WHERE item_id = NEW.item_id
AND related_id = NEW.related_id ;
IF ( rowCount > 0 ) THEN -- if( rowCount ) -- too works
-- DELETE FROM table
-- WHERE item_id = OLD.item_id
-- AND related_id = OLD.related_id;
-- ROLLBACK TRANSACTION; -- this is not allowed
set error_message =
concat( error_message, 'Update not allowed for ' );
set error_message =
concat( error_message, 'combination \'item_id=' );
set error_message =
concat( error_message, NEW.item_id, '\' and ');
set error_message =
concat( error_message, '\'related_id=', NEW.related_id, '\'' );
-- throw the error
-- Update not allowed for combination
-- 'itemid=1' and 'related_id=5' ( example )
signal sqlstate 23000 set message_text = error_message;
END IF;
END;//
delimiter ;

Related

How to write a stored procedure in MySQL that displays two messages when called

The goal here is to write a script attempts to insert a new category into categories table and if the insert was successful the message:
1 row was inserted
If the update is unsuccessful, the procedure should display this message:
Row was not inserted - duplicate entry
Whenever I run this script, I keep getting the second message no matter how many times I run it, when really it should display the first message, followed by the second. Here is my script. Can someone please help me spot what I'm overlooking? Thank you.
use my_guitar_shop;
/*drop procedure if exists add_category;*/
DELIMITER //
CREATE PROCEDURE add_category(
in categories VARCHAR(100)
)
begin
declare duplicate_entry_for_key TinyINT DEFAULT FALSE;
declare continue handler for 1062
SET duplicate_entry_for_key = TRUE;
insert into categories values (5, 'Electric');
select '1 row was inserted.' as message;
if duplicate_entry_for_key = true then
select 'Row was not inserted - duplicate entry.' as message;
end if;
end //
DELIMITER ;
/* call the stored procedure with 'Gibson' */
call add_category('Gibson');
call add_category('Gibson');
You should set duplicate_entry_for_key to True. If data insert is successful.
Do something like below after insert operation.
SET duplicate_entry_for_key = last_insert_id();
OR see the below example for bit check.
For example:
START TRANSACTION; -- Begin a transaction
INSERT INTO categories
VALUES
(
5
,'Electric'
);
IF ROW_COUNT() > 0 THEN -- ROW_COUNT() returns the number of rows updated/inserted/deleted
SET duplicate_entry_for_key = TRUE;
COMMIT; -- Finalize the transaction
ELSE
SET duplicate_entry_for_key = False;
--You can ROLLBACK the transaction also - Revert all changes made before the transaction began
END IF

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 ;

Return error message when duplicate unique fields

I want to return error message when duplicate records occur, groupName is unique field in group table.
I make like this, how can I make error handleing whithout using mysql-get-diagnostics because the server I am working on is version 5.0.77-log.
DELIMITER //
CREATE PROCEDURE addNewGroup(IN groupName varchar(128), IN addedBy INT,OUT message varchar(128) )
BEGIN
insert IGNORE into `group`( `group_name`,`Date_time_ added`,`added_by`) values (groupName ,CURRENT_TIMESTAMP(),addedBy) ;
END //
DELIMITER ;
I want to return error message when duplication occur?
Try this method -
BEGIN
INSERT IGNORE INTO table VALUES...;
IF ROW_COUNT() = 0 THEN
SET message = 'duplicate records occur';
END IF;
END
ROW_COUNT() function returns the number of inserted rows for the last statement.
In MySQL 5.5. you can use SIGNAL statement to generate a warning.

Get result occur from update statement inside stored procedures

I want to check the existance of specific record in db table, if it's exist then update if not I want to add new record
I am using stored procedures to do so, First I make update stetement and want to check if it occurs and return 0 then there's no record affected by update statement and that means the record does not exist.
I make like this
DELIMITER //
CREATE PROCEDURE revokePrivilegeFromUsers(IN userId int(11), IN privilegeId int(11), IN deletedBy int(11))
BEGIN
DECLARE isExist int;
isExist = update `user_privileges` set `mode` ='d' ,`updated_by` = deletedBy, `date_time_assigned` = CURRENT_TIMESTAMP() where `user_id`= userId and `privilege_id`=privilegeId;
IF isExist == 0 THEN
insert into `user_privileges`(`user_id`,`privilege_id`,`mode`,`date_time_assigned`,`updated_by`)values (userId ,privilegeId ,'d',CURRENT_TIMESTAMP(),deletedBy );
END IF;
END //
DELIMITER ;
This error occur with me
#1064 - 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 '= update `user_privileges` set `mode` ='d' ,`updated_by` = deletedBy, `date_time' at line 6
Is the way I am working is supported by mysql?
I solve the problem, I had 2 prblems
ROW_COUNT() is used to get the number of rows affected in insert, update or delete statements.
Equals comparison in stored procedure is = not ==
The correct stored procedure is
DELIMITER //
CREATE PROCEDURE revokePrivilegeFromUsers(IN userId int(11), IN privilegeId int(11), IN deletedBy int(11))
BEGIN
DECLARE count int default -1;
update `user_privileges` set `mode` ='d' ,`updated_by` = deletedBy, `date_time_assigned` = CURRENT_TIMESTAMP() where `user_id`= userId and `privilege_id`=privilegeId;
SELECT ROW_COUNT() into count ;
IF count = 0 THEN
insert into `user_privileges`(`user_id`,`privilege_id`,`mode`,`date_time_assigned`,`updated_by`)values (userId ,privilegeId ,'d',CURRENT_TIMESTAMP(),deletedBy );
END IF;
END //
DELIMITER ;
Use the INSERT IGNORE statement instead. I assume that your table has (user_id, privilege_id) as a unique key.
insert ignore into user_privileges (user_id,privilege_id,`mode,date_time_assigned,updated_by)
values (userId ,privilegeId ,'d',CURRENT_TIMESTAMP(),deletedBy )
on duplicate key update mode='d', date_time_assigned=now(),updated_by=deletedBy

Can I update the just added row using MySQL triggers

The default initial value of one column in my database is the same as the row's auto-incremented id. I'm trying to use triggers to set it.
CREATE TRIGGER `default_order_value`
AFTER INSERT ON `clusters`
FOR EACH ROW
BEGIN
UPDATE `clusters` SET `order` = NEW.id WHERE `id` = NEW.id;
END
But this keeps throwing a syntax error
#1064 - 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 '' at line 5
I've tried all sorts of permutations of this with no luck. Can anyone see what I'm doing wrong?
As zerkms said, you need to change the delimeter. But since you only use 1 line of code, you don't need the BEGIN and END. And that way, you don't need to change the delimiter either
CREATE TRIGGER `default_order_value`
AFTER INSERT ON `clusters`
FOR EACH ROW
UPDATE `clusters` SET `order` = NEW.id WHERE `id` = NEW.id;
Since you are getting an error you cannot update the row, I suggest the following:
Do NOT perform the update query at all. On default the order value = the ID value. So when the order value changes, you can update it properly.
If you are requesting the data with php, do something like this:
$order = $row['order'];
if ($order == '')
$order = $row['id'];
After you need it updating, you've got the correct value.
I don't think you can do that. An AFTER INSERT trigger cannot modify the same table, neither by issuing an UPDATE nor by something like this:
CREATE TRIGGER `default_order_value`
AFTER INSERT ON `clusters`
FOR EACH ROW
SET NEW.`order` = NEW.id ;
which results in this error:
> Error Code: 1362. Updating of NEW row is not allowed in after trigger
You can't either use a BEFORE INSERT trigger because then the NEW.id is not known (if you modify the above, the order column will get 0 value after the Insert.
What you can do, is use a transaction:
START TRANSACTION ;
INSERT INTO clusters (id)
VALUES (NULL);
UPDATE clusters
SET `order` = id
WHERE id = LAST_INSERT_ID();
COMMIT ;
You get the error because mysql treats ; in line 5 as the end of your trigger declaration, which obviously leads to the syntax error.
So you need to redefine delimiter before you specify the trigger body:
delimiter |
CREATE TRIGGER `default_order_value`
AFTER INSERT ON `clusters`
FOR EACH ROW
BEGIN
UPDATE `clusters` SET `order` = NEW.id WHERE `id` = NEW.id;
END;
|
delimiter ;
You can create just BEFORE INSERT TRIGGER, it's works like this:
CREATE TRIGGER `default_order_value`
BeFORE INSERT ON `clusters`
FOR EACH ROW
BEGIN
SET NEW.`order` = NEW.id ;
END
same as below we are using
DELIMITER $$
USE `e_store`$$
DROP TRIGGER /*!50032 IF EXISTS */ `Test`$$
CREATE
/*!50017 DEFINER = 'root'#'%' */
TRIGGER `Test` BEFORE INSERT ON `categories`
FOR EACH ROW
BEGIN
DECLARE vtype VARCHAR(250) DEFAULT NULL;
SET vtype = NEW.name;
IF (NEW.MDNAME IS NULL)
THEN
-- SET NEW.MDNAME = 'NA';
SET NEW.MDNAME=MD5(NEW.name);
END IF;
END;
$$
DELIMITER ;
This worked for me:
CREATE TRIGGER `update_table_2`
AFTER UPDATE ON `table_1`
FOR EACH ROW
UPDATE table2
JOIN table_1
SET table_2.the_column = NEW.the_column
WHERE table_2.auto_increment_field = OLD.auto_increment_field