CURSOR MYSQL TRIGGER - mysql

It's the first time I use cursors and im having issues with the variable 'done'. This variable handles whether the loop iterates over the cursosr or not. When the trigger its executed, the following error happens:
ERROR 1193 (HY000): Unknown system variable 'done'
As far as i see in the mysql cursor documentation, the 'done' variable declaration is correct. Anyone sees the problem?
The trigger:
delimiter //
CREATE TRIGGER `prestamo_positivo` AFTER UPDATE ON `prestamo`
FOR EACH ROW BEGIN
DECLARE aux VARCHAR(10);
DECLARE aux2 CHAR(10);
DECLARE c1 CURSOR FOR SELECT id_cliente FROM PRESTATARIO;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
SET aux=(SELECT A.id_cliente FROM PRESTATARIO A, PRESTAMO B WHERE A.numero_prestamo=OLD.numero);
IF(NEW.cantidad = 0) THEN
DELETE FROM `prestamo` WHERE numero=OLD.numero;
OPEN c1;
c1_loop: LOOP
fletch c1 into aux2;
IF done THEN LEAVE c1_loop; END IF;
IF(aux = aux2) THEN
INSERT INTO `mensaje`(mensaje, id_usuario) VALUES ("Renegociar prestamos del cliente",aux);
END IF;
END LOOP c1_loop;
CLOSE c1;
END IF;
END; //

If you check the sample code you linked in the question, you can see that done variable is explicitly declared before the continue handler, so you need to do the same. The below excerpt is from the sample code from the link you specified:
DECLARE done INT DEFAULT FALSE; //<= declare done variable here
DECLARE a CHAR(16);
DECLARE b, c INT;
DECLARE cur1 CURSOR FOR SELECT id,data FROM test.t1;
DECLARE cur2 CURSOR FOR SELECT i FROM test.t2;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
Also, as P.Salmon has already remarked, you cannot modify the table the trigger is declared on, therefore
DELETE FROM `prestamo` WHERE numero=OLD.numero;
line will fail with error 1442.

Related

cursor within a trigger

I am trying to make a cursor inside a trigger and the result of the cursor allows me to make inserts in another table, but I have problems with the NEW.id, I think that when I entered the cursor this parameter became empty .. how could it fix it?
when I send a static data it works without problem
I appreciate your help.
BEGIN
DECLARE finished INTEGER DEFAULT 0;
DECLARE _ingredient_id INTEGER;
DECLARE _unit_cost decimal(11,2);
DECLARE _quantity INTEGER;
DECLARE items CURSOR FOR
SELECT ingredients.id,ingredients.unit_cost,recipes.quantity
FROM sales, orders,detail_orders,recipes,ingredients
WHERE sales.id = NEW.id
AND orders.sale_id= NEW.id
AND detail_orders.order_id=orders.id
AND detail_orders.food_id=recipes.food_id
AND recipes.ingredient_id=ingredients.id;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET finished = 1;
OPEN items;
getItems: LOOP
FETCH items INTO _ingredient_id,_unit_cost,_quantity;
IF finished = 1 THEN
LEAVE getItems;
END IF;
SET #total= _unit_cost*_quantity;
INSERT INTO inventories (type,date,ingredient_id,quantity,unit_cost,total,sale_id) VALUES (2,now(),_ingredient_id,_quantity,_unit_cost,#total,NEW.id);
END LOOP getItems;
CLOSE items;
END
I managed to solve the problem with a stored procedure

what is wrong in this mysql cursor, and how to correct it?

CREATE PROCEDURE curLike()
BEGIN
DECLARE likeRec likecounttable;
DECLARE c_likeCount CURSOR FOR SELECT l.likeCount, l.qId FROM likecounttable l;
OPEN c_likeCount;
start_loop:LOOP
FETCH c_likeCount IN likeRec
UPDATE qentry SET qentry.likeCount = likeRec.likeCount WHERE qentry.qId=likeRec.qId;
END LOOP;
CLOSE c_likeCount;
END;
I am trying to use a cursor here which fetches records from likecounttable, I saw this type of syntax in few sites so I used it but it is not working
You are missing a semi-colon after your first declaration, furthermore, likecounttable is a table, not a data type.
Since you're trying to store two column values into your declared variables, your first line should look more like this
DECLARE likeRec_Count, likeRec_qId INT;
After reading your code, if you aren't adding to your cursor, you can simplify by using the following sql instead, which does the same thing as your cursor.
UPDATE qentry JOIN likecounttable l ON l.qId=qentry.qId
SET qentry.likeCount = l.likeCount
;
EDIT: If you wanted a complete update to your cursor, the following should do the same thing.
DELIMITER $$
CREATE PROCEDURE curLike()
BEGIN
DECLARE c_likeRec_isdone BOOLEAN DEFAULT FALSE;
DECLARE likeRec_Count, likeRec_qId INT;
DECLARE c_likeCount CURSOR FOR SELECT l.likeCount, l.qId FROM likecounttable l;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET c_likeRec_isdone = TRUE;
OPEN c_likeCount;
loop_likeRecList: LOOP
FETCH c_likeCount INTO likeRec_Count, likeRec_qId;
IF c_likeRec_isdone THEN
SET c_likeRec_isdone = FALSE;
LEAVE loop_likeRecList;
END IF;
UPDATE qentry SET qentry.likeCount = likeRec_Count WHERE qentry.qId=likeRec_qId;
END LOOP loop_likeRecList;
CLOSE c_likeCount;
END;
$$
CREATE PROCEDURE curLike()
BEGIN
DECLARE likeRec_Count, likeRec_qId INT;
DECLARE c_likeCount CURSOR FOR SELECT l.likeCount, l.qId FROM likecounttable l;
OPEN c_likeCount;
start_loop:LOOP
FETCH c_likeCount INTO likeRec_Count,likeRec_qId
UPDATE qentry SET qentry.likeCount = likeRec_Count WHERE qentry.qId=likeRec_qId ;
END LOOP;
CLOSE c_likeCount;
END;

Trying to add an additional value to an existing value in a column for all the rows that are retrieved using a stored procedure for

I have spent quite a long time to figure out.. so my update statement is affecting 0 rows although I know for a fact that it should affect at least affect more than a few rows as I have tried as a standalone. In place of update statement I tried select statement and it is working so does that mean that update statement is not supposed to work in stored procedure.. I kinda doubt it.. so I would like to get a second opinion.
my stored procedure code here:
DELIMITER $$
CREATE PROCEDURE updateKeywordsInRIConsole(in retailerId int )
BEGIN
declare key_words varchar(200) default null;
declare grpid bigint(20);
declare finished bool default false;
declare cur1 cursor for
select Keywords, GRPID
from RIConsole
where RetailerID = retailerId
and DateCreated > date(now()) - interval 1 year
and INSTR(Keywords, "offer_page") = false;
declare continue handler for not found set finished = 1;
declare exit handler for sqlexception
begin
show errors;
end;
declare exit handler for sqlwarning
begin
show warnings;
end;
open cur1;
start_loop: loop
fetch cur1 into key_words, grpid;
if finished = 1 then
leave start_loop;
end if;
update RIConsole set Keywords = concat(key_words, " ",
"offer_page") where GRPID = cast(grpid as signed); <-- this code not working...I called it with cast function to make sure.. and i also tried without it.
end loop start_loop;
close cur1;
END $$
DELIMITER ;
DROP PROCEDURE updateKeywordsInRIConsole;
Yes, you can do an UPDATE in a stored procedure.
If you are happy with your SELECT, you could do the while thing in a single statement. e.g.
CREATE PROCEDURE updateKeywordsInRIConsole(IN retailerId INT)
BEGIN
UPDATE RIConsole
SET Keywords = CONCAT(Keywords, " ", "offer_page")
WHERE where RetailerID = retailerId
AND DateCreated > DATE(NOW()) - INTERVAL 1 YEAR
AND INSTR(Keywords, "offer_page") = false;
END
;

MySQL: Solve my MySQL Cursor Error:1064

DROP TRIGGER IF EXISTS `ACTUALIZAR_LOTE_DETALLE`;
DELIMITER //
CREATE TRIGGER `ACTUALIZAR_LOTE_DETALLE` AFTER INSERT ON `detalle_envio`
FOR EACH ROW BEGIN
DECLARE ID_INVENTARIO_IN INT;
DECLARE ID_LOTE_DETALLE_MIN INT;
DECLARE CANTIDAD_A_COMPARAR INT;
DECLARE CANTIDAD_A_RESTAR INT;
SET CANTIDAD_A_RESTAR = new.cantidad_enviado;
SET ID_INVENTARIO_IN = (Select id_inventario from detalle_requisicion WHERE id_detalle_requisicion = new.id_detalle_requisicion);
DECLARE cur_id CURSOR FOR SELECT id_lote_detalle from lote_detalle WHERE id_inventario = ID_INVENTARIO_IN AND cantidad > 0;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET DONE = 1;
OPEN cur_id;
read_loop: LOOP
FETCH cur_id INTO LOTE_DETALLE_IDS;
SET CANTIDAD_A_COMPARAR = select cantidad from lote_detalle where id_lote_detalle = LOTE_DETALLE_IDS;
IF CANTIDAD_A_RESTAR > CANTIDAD_A_COMPARAR THEN
UPDATE `lote_detalle` SET cantidad=cantidad-CANTIDAD_A_COMPARAR WHERE id_lote_detalle = LOTE_DETALLE_IDS;
SET CANTIDAD_A_RESTAR = CANTIDAD_A_RESTAR - CANTIDAD_A_COMPARAR;
ELSE
UPDATE `lote_detalle` SET cantidad=cantidad-CANTIDAD_A_RESTAR WHERE id_lote_detalle = LOTE_DETALLE_IDS;
LEAVE read_loop;
END IF;
IF DONE = 1 THEN
LEAVE read_loop;
END IF;
END LOOP read_loop;
CLOSE cur_id;
END
//
DELIMITER ;
Is anything in the cursor?
the query or something?
Is anything in the Syntax?
Cause i have based this Trigger from the syntax of the MySQL Forums
This is the 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 'DECLARE cur_id CURSOR FOR SELECT id_lote_detalle from lote_detalle WHERE id_inve' at line 9
There are at least following issues with your code:
All declarations should come before any statements (SET in your case). That's what is causing your immediate error.
You didn't declare DONE variable
You didn't declare LOTE_DETALLE_IDS variable, but you declared ID_LOTE_DETALLE_MIN which you didn't use. Perhaps you should rename ID_LOTE_DETALLE_MIN to LOTE_DETALLE_IDS then.
Since declarations go first you have to re-write your cursor definition (e.g. with subquery) because ID_INVENTARIO_IN variable isn't initialized yet
Your whole declaration section can be re-written in a following manner
-- declaration of variables of the same type can be combined
DECLARE ID_INVENTARIO_IN,
LOTE_DETALLE_IDS,
CANTIDAD_A_COMPARARINT,
CANTIDAD_A_RESTAR,
DONE INT;
DECLARE cur_id CURSOR FOR ...
DECLARE CONTINUE HANDLER ...
-- initialize variables
SET ...
Now, I believe your trigger most likely can be re-written with just update statement(s). You can get help with it if you explain in plain words what you're trying to achieve and provide exact table schemas and sample data.

How to use many cursors in a single store procedure in MySQL

I want to create a store procedure that has ability to do multi-tasks. Then it got error message below
Error Code: 1338 Cursor declaration after handler declaration
Please look my store procedure
CREATE PROCEDURE `spTest`(OUT v1 VARCHAR(500), OUT v2 VARCHAR(500))
BEGIN
DECLARE _cur_1 CURSOR FOR
SELECT id
FROM tbl_1;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET _cur1Done = 1;
DECLARE _cur_2 CURSOR FOR
SELECT id
FROM tbl_2;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET _cur2Done = 1;
.......
You may only have one continue handler active in a block. Multiple continue handler's get the Duplicate handler declared in same block issue, even if you get the order of declarations correct ( all cursors first, then the handlers later).
You can work around this by creating a block devoted to a cursor's access. You can place these chunks of code in the same parent block, or in nested loops, or wherever.
DELIMITER $$
CREATE PROCEDURE `spTest`(OUT v1 VARCHAR(500), OUT v2 VARCHAR(500))
BEGIN
declare tbl_1_id int;
declare tbl_2_id int;
declare _cur1Done boolean default false;
declare _cur2Done boolean default false;
DECLARE _cur_1 CURSOR FOR SELECT id FROM tbl_1;
DECLARE _cur_2 CURSOR FOR SELECT id FROM tbl_2;
begin -- dedicated block to fetch from cursor 1 and update its flag
declare continue handler for not found set _cur1Done = TRUE;
fetch _cur_1 into tbl_1_id;
end;
begin -- dedicated block to fetch from cursor 2 and update its flag
declare continue handler for not found set _cur2Done = TRUE;
fetch _cur_2 into tbl_2_id;
end;
END $$
DELIMITER ;
In the manual it says
Cursor declarations must appear before handler declarations and after variable and condition declarations.
Also you can not have multiple continue handlers (how should MySQL know which continue handler is related to which cursor? Unfortunately you can't specify that), unless you nest them, for example like this:
DELIMITER $$
CREATE PROCEDURE `spTest`(OUT v1 VARCHAR(500), OUT v2 VARCHAR(500))
BEGIN
BLOCK1:BEGIN
DECLARE variable1 INT;
DECLARE _cur_1 CURSOR FOR SELECT id FROM tbl_1;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET _cur1Done = 1;
LOOP1: LOOP
FETCH _cur_1 INTO variable1;
IF _cur1Done THEN
CLOSE _cur_1;
LEAVE LOOP1;
END IF;
BLOCK2:BEGIN
DECLARE variable2 INT;
DECLARE _cur_2 CURSOR FOR SELECT id FROM tbl_2;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET _cur2Done = 1;
OPEN _cur_2;
LOOP2: LOOP
FETCH _cur_2 INTO variable2;
IF _cur2Done THEN
CLOSE _cur_2;
LEAVE LOOP2;
END IF;
END LOOP LOOP2;
END BLOCK2;
END LOOP LOOP1;
END BLOCK1;
END $$
DELIMITER ;