I'm sort of new to SQL and stored procedures. I'm trying to set some variables to use later on in a transaction while also setting a rollback variable on an exception, but I'm not able to do this.
I don't know where the error is because when I switch the how_many section after the _rollback, The error changes.
What's wrong with the way I'm declaring variables here?
DELIMITER $$
DROP PROCEDURE IF EXISTS `do_thing` $$
CREATE PROCEDURE `do_thing`()
BEGIN
DECLARE how_many INT;
SELECT COUNT(*) FROM things INTO how_many;
-- Prepare roleback.
DECLARE `_rollback` BOOL DEFAULT 0;
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET `_rollback` = 1;
-- Start transaction.
START TRANSACTION;
-- Do all the things.
IF `_rollback` THEN
ROLLBACK;
ELSE
COMMIT;
END IF;
END $$
DELIMITER ;
EDIT:
when I declare variables like this:
DECLARE how_many INT;
-- Prepare roleback.
DECLARE `_rollback` BOOL DEFAULT 0;
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET `_rollback` = 1;
SELECT COUNT(*) FROM tiers INTO how_many;
It seems to be fine. Am I not allowed to declare more variables after certain variables in a procedure are set?
From the Manual Page DECLARE Syntax:
DECLARE is permitted only inside a BEGIN ... END compound statement
and must be at its start, before any other statements.
Declarations must follow a certain order. Cursor declarations must appear before handler declarations. Variable and condition
declarations must appear before cursor or handler declarations.
Note that these are called Local Variables in mysql. These differ from User Variables (prefixed with the # symbol), which can be sprinkled about with your statements and do not require a DECLARE.
The DECLARE statements are only permitted after BEGIN, if you try to add another DECLARE after that it shows error.
Move the line SELECT COUNT(*) FROM things INTO how_many; after all DECLARE lines.
DELIMITER $$
DROP PROCEDURE IF EXISTS `do_thing` $$
CREATE PROCEDURE `do_thing`()
BEGIN
DECLARE how_many INT;
-- Prepare roleback.
DECLARE `_rollback` BOOL DEFAULT 0;
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET `_rollback` = 1;
-- Start transaction.
START TRANSACTION;
SELECT COUNT(*) FROM things INTO how_many;
-- Do all the things.
IF `_rollback` THEN
ROLLBACK;
ELSE
COMMIT;
END IF;
END $$
DELIMITER ;
Related
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.
I am reading an article on stored procedures and this is the code:
delimiter //
create procedure largest_order(out largest_id int)
begin
declare this_id int;
declare this_amount float;
declare l_amount float default 0.0;
declare l_id int;
declare done int default 0;
declare continue handler for sqlstate '02000' set done = 1;
declare c1 cursor for select orderid, amount from orders;
open c1;
repeat
fetch c1 into this_id, this_amount;
if not done then
if this_amount > l_amount then
set l_amount=this_amount;
set l_id=this_id;
end if;
end if;
until done end repeat;
close c1;
set largest_id=l_id;
end
//
delimiter ;
I am using a simple database named "mydatabase". After running the above code it gives me this error: ERROR 1338 (42000): Cursor declaration after handler declaration
What is wrong and how can I fix it?
This is my first time working with stored procedures.
Per MySql docs:
Cursor declarations must appear before handler declarations and after
variable and condition declarations.
So I updated the code as follows:
delimiter //
create procedure largest_order(out largest_id int)
begin
declare this_id int;
declare this_amount float;
declare l_amount float default 0.0;
declare l_id int;
-- 1. cursor finished/done variable comes first
declare done int default 0;
-- 2. the curser declaration and select
declare c1 cursor for select orderid, amount from orders;
-- 3. the continue handler is defined last
declare continue handler for sqlstate '02000' set done = 1;
open c1;
repeat
fetch c1 into this_id, this_amount;
if not done then
if this_amount > l_amount then
set l_amount=this_amount;
set l_id=this_id;
end if;
end if;
until done end repeat;
close c1;
set largest_id=l_id;
end
//
delimiter ;
And now works fine.
How can I use two cursors in the same routine? I am getting "Variable or condition declaration after cursor or handler declaration" error while creating procedure.
I have to use both integer outerDone & innerDone to check whether cursor points to null or not.
I haven't worked on stored-procedure yet. Could anybody sort this problem please. Thanks in advance!!
DELIMITER ##;
create procedure updateStopTimeColumn()
BEGIN
DECLARE outerDone INT DEFAULT 0;
DECLARE vehicle_record CURSOR FOR SELECT `vehicleId` FROM `vehicle`;
DECLARE current_record CURSOR FOR SELECT `id`,`tsTime`,`teTime` FROM `trip` where `vehicleId`=vehId order by `tsTime`;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET outerDone = 1;
DECLARE vehId,tripId CHAR(250);
DECLARE currentTsTime,currentTeTime time;
OPEN vehicle_record;
REPEAT
FETCH vehicle_record INTO vehId;
block2 : BEGIN
DECLARE innerDone INT DEFAULT 0;
DECLARE tempTripId CHAR(250);
DECLARE tempTsTime,tempTeTime time;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET innerDone = 1;
OPEN current_record;
FETCH current_record INTO tempTripId, tempTsTime,tempTeTime;
REPEAT
FETCH current_record INTO tripId,currentTsTime,currentTeTime;
UPDATE trip set stopTime=(currentTeTime-tempTsTime) where id=tempTripId and tempTeTime IS NOT NULL;
SET tempTripId=tempId;
SET tempTsTime=currentTsTime;
SET tempTeTime=currentTeTime;
UNTIL innerDone END REPEAT;
END block2;
CLOSE current_record;
UNTIL outerDone END REPEAT;
CLOSE vehicle_record;
END; ##
DELIMITER;
Have you tried moving the
DECLARE vehId,tripId CHAR(250);
DECLARE currentTsTime,currentTeTime time;
statements to before your cursors?
I run this code as a sql script from command line, and I get "you have an error in your SQL syntax" almost in all lines! Any ideas what is wrong here?
CREATE PROCEDURE updatemandate()
BEGIN
DECLARE _mandate_id BIGINT(20);
DECLARE _has_succesful_payment tinyint(1);
DECLARE done INT DEFAULT 0;
DECLARE cnt INT;
DECLARE mandateCursor CURSOR FOR Select mandate_id, has_succesful_payment From mandates;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
OPEN mandateCursor;
allmandates: LOOP
Fetch mandateCursor INTO _mandate_id, _has_succesful_payment;
IF done THEN LEAVE allmandates;
END IF;
Select COUNT(*) FROM payments WHERE mandate_id=_mandate_id AND status='OK' into cnt;
IF cnt>0 THEN
SET _has_succesful_payment=1;
END IF;
END LOOP allmandates;
CLOSE mandateCursor;
END
The ; character is the default delimiter, so when MySQL sees the first ; it thinks you are done. When you create a sproc, you need to declare a different delimiter character, like so:
DELIMITER $$
CREATE PROCEDURE updatemandate()
READS SQL DATA
BEGIN
DECLARE _mandate_id BIGINT(20);
DECLARE _has_succesful_payment tinyint(1);
DECLARE done INT DEFAULT 0;
DECLARE cnt INT;
DECLARE mandateCursor CURSOR FOR Select mandate_id, has_succesful_payment From mandates;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
OPEN mandateCursor;
allmandates: LOOP
Fetch mandateCursor INTO _mandate_id, _has_succesful_payment;
IF done THEN LEAVE allmandates;
END IF;
Select COUNT(*) FROM payment WHERE mandate_id=_mandate_id AND status='OK' into cnt;
IF cnt>0 THEN
SET _has_succesful_payment=1;
END IF;
END LOOP allmandates;
CLOSE mandateCursor;
END$$
DELIMITER ;
Also a good idea to add the READS SQL DATA to the sproc definition in case you need to support binary logging.
Have you tried putting delimiter // before the CREATE statement and change the last line with END //?
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 ;