How to avoid running one more time using CURSOR in MySQL? - mysql

I have used a cursor in MySQL but it always runs one more time then I expect.
Code like this:
drop PROCEDURE if exists test_sp;
DELIMITER //
CREATE PROCEDURE test_sp()
BEGIN
DECLARE varid int;
DECLARE varExit_loop BOOLEAN;
DECLARE cursor_name CURSOR FOR
SELECT id
FROM test;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET varExit_loop = TRUE;
drop table if exists test;
create table test
(id int);
insert into test
values
(1),
(2),
(3);
OPEN cursor_name;
test_loop: LOOP
FETCH cursor_name INTO varid;
select varid;
IF varExit_loop THEN
CLOSE cursor_name;
LEAVE test_loop;
END IF;
END LOOP test_loop;
END; //
DELIMITER ;
After I run the SP, it will return 4 results, 1,2,3 and one more 3.
Is there any way to avoid having one more 3?

I have moved
IF varExit_loop THEN
CLOSE cursor_name;
LEAVE test_loop;
END IF;
END LOOP test_loop;
right under
FETCH cursor_name INTO varid;
and it worked fine.

Related

MySQL puzzle: How to combine result in Loop?

So far I have tried using CREATE VIEW <view_table_name> and
CREATE TABLE <table_name> and CREATE TEMPORARY <temporary_table_name>
and this methods are taking so much time because I have to (step 1) create a table and (step 2) insert a data. Then (step 3) select the created table. Finally, (step 4) drop the table.
This is my procedure: (My program doesn't look make any sense, I mean why am I doing this. The answer to your confusion is, this is just a part of my large code and I made it this way so that it may look simple.)
BEGIN
-- Main loop variables
DECLARE col_Name varchar(255);
DECLARE col_Description varchar(255);
-- Main Loop
Block2: BEGIN
DECLARE done BOOLEAN DEFAULT FALSE;
DECLARE statement CURSOR FOR SELECT `name`, `description` FROM `rules`;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
OPEN statement;
REPEAT
MainLoop: LOOP
FETCH statement INTO col_Name, col_Description;
IF done THEN
LEAVE MainLoop;
END IF;
SELECT col_Name AS `Name`, col_Description AS `Description`;
END LOOP MainLoop;
UNTIL done END REPEAT;
CLOSE statement;
END Block2;
-- End of Main Loop
END
The result is:
The problem:
The result is separate. How do I combine this result into one table?
Declare a temporary table at the beginning ,populate data into temp table and then access it after loop ends
BEGIN
-- Main loop variables
DECLARE col_Name varchar(255);
DECLARE col_Description varchar(255);
create temporary table yourtable (name varchar(50),description varchar(50));
-- Main Loop
Block2: BEGIN
DECLARE done BOOLEAN DEFAULT FALSE;
DECLARE statement CURSOR FOR SELECT `name`, `description` FROM `rules`;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
OPEN statement;
REPEAT
MainLoop: LOOP
FETCH statement INTO col_Name, col_Description;
IF done THEN
LEAVE MainLoop;
END IF;
insert into yourtable(name,description)
values (col_Name, col_Description)
END LOOP MainLoop;
UNTIL done END REPEAT;
CLOSE statement;
select * from yourtable
END Block2;
-- End of Main Loop
END
I think things can be easier.
you don't have to use a cursor, you just need to create an empty table in your procedure, and input the result from your query.
like below:
CREATE DEFINER=`root`#`localhost` PROCEDURE `temp`(val int)
BEGIN
DECLARE yourval datatype()
-- declare as many as you need.---
set #beginning = 0
set #ending = val
-- how many time do you need to repeat your code?
---CREATE AN EMPTY TABLE HERE---
while #beginning < #ending do
insert table TableCreatedAbove
---YOUR QUERY---
---YOU MAY NEED TO DO SOMETHING WITH YOUR DECLARED VARIABLE---
set #beginning = #beginning + 1;
end while;
commit;
END
when you need to use your combined table, just
select * from TableCreatedAbove
maybe you already solved your problem in somewhere else, but here is my answer.

for each loop in trigger

Unable to use for each loop in trigger
I am getting
Error Code: 1064
DELIMITER $$
CREATE
TRIGGER `TRG_AU_DEVICES_HOWLONG` AFTER UPDATE ON `devices`
FOR EACH ROW BEGIN
DECLARE lastid INTEGER;
DECLARE a, b, c VARCHAR(255);
SET #lastid := (SELECT deviceId FROM devices ORDER BY packetDate DESC LIMIT 1);
DECLARE cur1 CURSOR FOR SELECT alertType,deviceId FROM alerts WHERE alerts.deviceId = #lastid ;
OPEN cur1;
read_loop: LOOP
FETCH cur1 INTO a, b;
insert into test(alertType,deviceId) values(a,b);
END LOOP;
CLOSE cur1;
END;
$$
DELIMITER ;
Unable to use for each loop in trigger, am getting Error Code: 1064.How to use for each loop in trigger
DELIMITER $$
CREATE
TRIGGER TRG_TEST BEFORE UPDATE ON devices
FOR EACH ROW BEGIN
DECLARE done INT DEFAULT 0;
DECLARE alert VARCHAR(255);
DECLARE my_cur CURSOR FOR SELECT alertType FROM alerts WHERE deviceId = 8170610948;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
OPEN my_cur;
my_cur_loop:
LOOP FETCH my_cur INTO a;
IF done = 1 THEN
LEAVE my_cur_loop;
END IF;
INSERT INTO test (alertType)VALUES(alert);
END LOOP my_cur_loop;
CLOSE my_cur;
END;
$$
You are referencing your own looped array inside the array. I call this a loop bomb.
Since you expect the statement inside the loop to return control to the loop, but the flow of logic can not reversed halfway through a loop mechanically.It can be conditioned, not reversed because the array is not expressed, it is defined.
To Achieve logical expression of the array before the construct, consider using, predefined data matrices, lambda functions or Using Lambda programming that includes meta or reflective paradigms. Lisp, GUILE, OCAMEL are examples.

Get Columns Single Record in Stored Procedure Mysql

I have my procedure and is working, but my question is the following,
with the cursor is working correctly, but before the cursor I need a Single Record with several columns, I donĀ“t know if I need another cursor just for one record.
Which would be the right way to get the columns of that single row without a cursor.
The query to execute is:
'SELECT id,anio,fec_iniciointeres FROM mytable WHERE id=3 '
DELIMITER $$
DROP PROCEDURE IF EXISTS db.cal_intereses$$
CREATE DEFINER=`root`#`localhost` PROCEDURE `cal_intereses`()
BEGIN
DECLARE factura_id INT UNSIGNED;
DECLARE val_avaluo DECIMAL(16,2);
DECLARE fec_actual DATE;
DECLARE done INT;
DECLARE cur CURSOR FOR SELECT fac_facturas.id AS factura_id, fac_facturas.val_avaluo FROM fac_facturas WHERE fac_facturas.vigencia_id<=26 AND fac_facturas.estado=1 AND fac_facturas.val_avaluo>0 LIMIT 10;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done=1;
SET fec_actual=(SELECT CURDATE());
SET done = 0;
OPEN cur;
ciclo: LOOP
FETCH cur INTO factura_id,val_avaluo;
IF done=1 THEN LEAVE ciclo; END IF;
DELETE FROM val_interesaux;
IF fec_actual>='2006-07-29' THEN
INSERT INTO val_interesaux(factura_id,fec_inicio) VALUES(factura_id,fec_actual);
END IF;
END LOOP ciclo;
CLOSE cur;
END$$
DELIMITER ;

Cursor incorrect integer value error

I am trying to make my first cursor in MySQL and I am receiving an error. It says incorrect integer value. I was thinking this would grab the value in row one from column customer_Id, and store it into the IdValue variable. How do I code this correctly and fix this error?
DELIMITER $$
CREATE PROCEDURE CursorProcedure()
BEGIN
DECLARE IdValue int;
DECLARE myCursor CURSOR FOR
SELECT customer_Id FROM customers;
OPEN myCursor;
FETCH myCursor INTO IdValue;
CLOSE myCursor;
SELECT IdValue;
END$$
DELIMITER ;
DROP PROCEDURE IF EXISTS CursorProcedure;
DELIMITER $$
CREATE PROCEDURE CursorProcedure()
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE IdValue int;
DECLARE myCursor CURSOR FOR SELECT customer_Id FROM customers;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN myCursor;
read_loop: LOOP
FETCH myCursor INTO IdValue;
IF done THEN
LEAVE read_loop;
END IF;
--
-- YOU ARE IN YOUR READ LOOP
-- DO SOMETHING WITH IT HERE
--
END LOOP;
CLOSE myCursor;
END$$
DELIMITER ;
But in the "do something with it here", don't do a SELECT on it because it will generate multiple result sets. Do something meaningful.
Manual page on Cursors.
As an aside, cursors are rarely your friends. They are extremely slow. Use them in dire emergencies only.

Printing out multiple rows in stored procedure

I want to pass in an int value and get back a list of short passwords that are less than this int. I have had trouble modifying the MySQL cursors example: http://dev.mysql.com/doc/refman/5.0/en/cursors.html. I want to print out the rows with short passwords but any time the result has more than one row PhpMyAdmin just displays "Processing request" indefinitely. What am I doing wrong? Its ruining my Saturday productivity!
DELIMITER $$
CREATE DEFINER=`root`#`localhost` PROCEDURE `ShortPasswd`(IN `passwordLength` TINYINT)
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE a INT;
DECLARE b VARCHAR(255);
DECLARE cur1 CURSOR FOR SELECT UID, Password
FROM authentication WHERE CHARACTER_length(Password) < passwordLength;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN cur1;
read_loop: LOOP
FETCH cur1 INTO a, b;
IF done THEN
LEAVE read_loop;
END IF;
SELECT b;
END LOOP;
CLOSE cur1;
END
$$
DELIMITER ;
DELIMITER $$
CREATE DEFINER=`root`#`localhost` PROCEDURE `ShortPasswd`(IN `passwordLength` TINYINT)
BEGIN
SELECT UID, Password
FROM authentication WHERE CHARACTER_length(Password) > passwordLength;
END
$$
DELIMITER ;
You will get rows UID, password directly. as you get from a query when you
call ShortPasswd;
I suggest to modify these lines:
...
DECLARE FINISHED BOOLEAN DEFAULT 0;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
...
IF done THEN
CLOSE cur1;
LEAVE read_loop;
END IF;
...
END LOOP read_loop;
I use the 0/1 instead of FALSE/TRUE, and close the cursor before leave the LOOP.