MySQL stored procedure cursor not working - mysql

MySQL 5.5
CREATE TABLE `card` (
`id` int,
`cardnumber` varchar(100),
`customer` varchar(100),
PRIMARY KEY (`id`)
);
INSERT INTO `card` VALUES (1, '5000', 'Google');
DELIMITER //
CREATE PROCEDURE `test` ()
BEGIN
DECLARE finished INTEGER DEFAULT 0;
DECLARE cardnumber varchar(20) DEFAULT "";
DECLARE cursor1 CURSOR FOR SELECT cardnumber FROM card WHERE customer = 'Google';
DECLARE CONTINUE HANDLER FOR NOT FOUND SET finished = 1;
OPEN cursor1;
get_card: LOOP
FETCH cursor1 INTO cardnumber;
IF finished = 1 THEN
LEAVE get_card;
END IF;
END LOOP get_card;
SELECT cardnumber;
CLOSE cursor1;
END //
DELIMITER ;
CALL test();
Returns no results, what did I do wrong?

There would be a ambiguity in identifying a column and a local variable if they are defined a same name. It IS always better practice to define a different naming convention in stored procedures, functions and trigger bodies when dealing with database object names like column.
To cross check, execute following procedure and see the result.
delimiter //
drop procedure if exists `same_name_test` //
create procedure `same_name_test`()
begin
DECLARE cardnumber varchar(20) DEFAULT "";
-- this statement will print empty string
SELECT cardnumber as cn
FROM card
WHERE customer = 'Google';
end;
//
delimiter ;
call `same_name_test`;
+------+
| cn |
+------+
| |
+------+
Modified Stored Procedure:
DELIMITER //
drop procedure if exists `test` //
CREATE PROCEDURE `test`()
BEGIN
DECLARE finished INTEGER DEFAULT 0;
DECLARE card_number varchar(20) DEFAULT "";
DECLARE cursor1 CURSOR FOR
SELECT cardnumber
FROM card_so_q23811277
WHERE customer = 'Google';
DECLARE CONTINUE HANDLER FOR NOT FOUND SET finished = 1;
OPEN cursor1;
get_card: LOOP
-- read cursor into new local variable
FETCH cursor1 INTO card_number;
IF finished = 1 THEN
LEAVE get_card;
END IF;
END LOOP get_card;
-- using new name of variable
SELECT card_number;
CLOSE cursor1;
END;
//
DELIMITER ;
Now call the Stored Procedure:
CALL `test`;
+-------------+
| card_number |
+-------------+
| 5000 |
+-------------+

Related

MariaDB and stored procedure: Why does the cursor iterate the last row twice?

MariaDB and stored procedure: Why does the cursor iterate the last row twice using?
I have a table tab which for simplicity contains only one column id with numbers from 1 to 3 (see part 1 of the code below). I created a cursor (3) to retrieve each number from tab and entered the number as IN parameter into a short procedure (sp, part 3). The sp inserts the parameter into the table tab_ctrl (ctrl for controlling).
After runnig the cursor (4) and selecting the content of tab_ctrl I got this result:
+----+
| id |
+----+
| 1 |
| 2 |
| 3 |
| 3 | <- why?
+----+
The last row of tab has been iterated twice. Why the cursor doesn't stop after the third row?
-- **************************************
-- (1) Prepare tables
-- **************************************
use test0;
-- Table to read each entry
DROP TABLE IF EXISTS tab;
CREATE TABLE tab (
id INTEGER UNSIGNED DEFAULT NULL
, PRIMARY KEY (id)
) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci
;
INSERT INTO tab VALUES
(1)
,(2)
,(3)
;
-- Table for controlling
DROP TABLE IF EXISTS tab_ctrl;
CREATE TABLE tab_ctrl (
id INTEGER UNSIGNED DEFAULT NULL
) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci
;
-- **************************************
-- (4) Run sp
-- **************************************
CALL sp_cursor_add_records();
SELECT * FROM tab_ctrl;
-- **************************************
-- (2) Stored procedure
-- **************************************
DELIMITER //
DROP PROCEDURE IF EXISTS sp_add_records //
CREATE PROCEDURE sp_add_records(IN p_id INTEGER UNSIGNED)
BEGIN
INSERT INTO tab_ctrl
SELECT p_id
;
END//
DELIMITER ;
-- **************************************
-- (3) Cursor
-- **************************************
DELIMITER //
DROP PROCEDURE IF EXISTS sp_cursor_add_records //
CREATE PROCEDURE sp_cursor_add_records()
BEGIN
-- Local variables
DECLARE done BOOLEAN DEFAULT 0;
DECLARE p_id INTEGER UNSIGNED;
-- Cursor
DECLARE c CURSOR
FOR
SELECT id
FROM tab
ORDER BY id
;
-- Declare continue handler
DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done=1;
-- Open cursor
OPEN c;
-- Loop through all rows
REPEAT
-- Get record
FETCH c INTO p_id;
-- Call add record procedure
CALL sp_add_records(p_id);
-- End of loop
UNTIL done END REPEAT;
-- Close cursor
CLOSE c;
END//
DELIMITER ;
If you restructure your loop to only CALL sp_ad_records(p_id) if its not done:
-- Declare continue handler
DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done=1;
-- Open cursor
OPEN c;
-- Get First record
FETCH c INTO p_id;
-- Loop through all rows
WHILE NOT done DO
-- Call add record procedure
CALL sp_add_records(p_id);
-- Get next record
FETCH c INTO p_id;
-- End of loop
END WHILE;
-- Close cursor
CLOSE c;

Using Loop and Cursor in same mysql procedure showing error

The following code is working properly. But when i am enabling the commented area (cursor), then the code showing error. Please help to fix the issue.
Scenario: The code allow some parameter. It will prepare the data in a table and then the cursor will take data from that table and output that data.
Same Parameter: call prGetInsuranceData_Multiple(2, 'Saroar,Ahmed', '20,30')
DELIMITER $$
USE `surokkha_db`$$
DROP PROCEDURE IF EXISTS `prGetInsuranceData_Multiple`$$
CREATE DEFINER=`root`#`localhost` PROCEDURE `prGetInsuranceData_Multiple`
(
PeopleToBeCovered INT,
IN NAME VARCHAR(4000),
IN AGE VARCHAR(200)
)
BEGIN
-- declare loop variables
DECLARE V_Name VARCHAR(255);
DECLARE V_AGE INT;
DECLARE X INT DEFAULT 0;
-- declare cursor variables
DECLARE Cur_Finished INTEGER DEFAULT 0;
DECLARE Cur_Name VARCHAR(255);
DECLARE Cur_Age INT;
-- create a table with comma separated values (Name with age in table format)
CREATE TEMPORARY TABLE TempCustomer
(
NAME VARCHAR(255),
AGE INT
);
CREATE TEMPORARY TABLE TempCustomer1
(
NAME VARCHAR(255),
AGE INT
);
SET X = 1;
BEGIN
WHILE X <= PeopleToBeCovered DO
SET V_Name = SUBSTRING_INDEX(SUBSTRING_INDEX(NAME,',',X),',',-1);
SET V_Age = SUBSTRING_INDEX(SUBSTRING_INDEX(AGE,',',X),',',-1);
SET X = X + 1;
INSERT INTO TempCustomer VALUES(V_Name, V_Age);
END WHILE;
END;
/*
-- declare cursor
DECLARE cur_NameWithAge
CURSOR FOR
SELECT NAME, AGE FROM TempCustomer;
-- declare NOT FOUND handler
DECLARE CONTINUE HANDLER
FOR NOT FOUND SET finished = 1;
OPEN cur_NameWithAge;
GetNameWithAge: LOOP
FETCH cur_NameWithAge INTO Cur_Name, Cur_Age;
IF finished = 1 THEN
LEAVE GetNameWithAge;
END IF;
-- get data and insert into table
INSERT INTO TempCustomer1 VALUES(Cur_Name, Cur_Age);
END LOOP GetNameWithAge;
CLOSE cur_NameWithAge;
*/
SELECT * FROM TempCustomer;
-- as after setting cursor the data is not needed, thats why drop the tables
DROP TEMPORARY TABLE TempCustomer;
DROP TEMPORARY TABLE TempCustomer1;
END$$
DELIMITER ;
You need to pout the loop and its declarations in a BEGIn ENd
Still i needed to reprogram the hole thing, because your code threw error, that i couldn't find
create procedure prGetInsuranceData_Multiple(
IN PeopleToBeCovered INT,
IN _NAME VARCHAR(4000),
IN _AGE VARCHAR(200))
begin
DECLARE V_Name VARCHAR(255);
DECLARE V_AGE INT;
DECLARE X INT DEFAULT 0;
drop temporary table if exists TempCustomer;
drop temporary table if exists TempCustomer1;
create temporary table TempCustomer( NAME VARCHAR(255),AGE int );
create temporary table TempCustomer1(NAME VARCHAR(255),AGE int);
SET X = 1;
BEGIN
WHILE X <= PeopleToBeCovered DO
SET V_Name = SUBSTRING_INDEX(SUBSTRING_INDEX(_NAME,',',X),',',-1);
SET V_Age = SUBSTRING_INDEX(SUBSTRING_INDEX(_AGE,',',X),',',-1);
SET X = X + 1;
INSERT INTO TempCustomer VALUES(V_Name,V_Age);
END WHILE;
END;
BEGIN
DECLARE finished INTEGER DEFAULT 0;
DECLARE v_id int ;
DECLARE v_name varchar(255);
declare cur_NameWithAge cursor for select NAME,AGE from TempCustomer;
DECLARE CONTINUE HANDLER
FOR NOT FOUND SET finished = 1;
open cur_NameWithAge;
GetNameWithAge: LOOP
fetch cur_NameWithAge into v_name,v_id;
IF finished = 1 THEN
LEAVE GetNameWithAge;
END IF;
INSERT INTO TempCustomer1 VALUES (v_name,v_id);
END LOOP GetNameWithAge;
CLOSE cur_NameWithAge;
END;
select * FROM TempCustomer;
DROP TEMPORARY TABLE TempCustomer1;
DROP TEMPORARY TABLE TempCustomer1;
end
call prGetInsuranceData_Multiple(2, 'Saroar,Ahmed', '20,30')
NAME | AGE
:----- | --:
Saroar | 20
Ahmed | 30
db<>fiddle here

MySQl storage procedures

I have a little problem. Looks like the procedure does not exist. Somehow it's dropped after the creation. I get different error each time i change something. I'm not really sure what's causing the error, maybe I'm not allowed to drop procedures and creating them in the same query.
I hope you guys can help me out.
drop procedure if exists refIntChk;
DELIMITER //
CREATE PROCEDURE refIntChk(IN district INT(11), OUT b INT(1))
BEGIN
DECLARE b INT(1);
IF district IN (select dist FROM t13)
THEN
SET b = 1;
ELSE
SET b = 0;
END IF;
END; //
DELIMITER ;
drop procedure gen if exists ;
DELIMITER //
CREATE PROCEDURE gen()
BEGIN
DECLARE rows INT(11) DEFAULT (SELECT COUNT(dist) FROM t13);
DECLARE district INT(11);
DECLARE custname VARCHAR(16);
DECLARE revenue FLOAT;
DECLARE x INT DEFAULT 10000;
DECLARE outvar INT(11);
WHILE x > 0
DO
SET district = FLOOR(RAND()*rows)+1;
CALL refIntChk(district, outvar);
IF outvar = 1
THEN
SET custname = substring(MD5(RAND()), -16);
SET revenue = (RAND() * 10);
INSERT INTO t14 VALUES(NULL, custname, district, revenue);
SET x = x - 1;
END IF;
END WHILE;
END;//
DELIMITER ;
CALL gen();
When you get errors, it's usually good to run each statement, one by one, and see which one is producing the error.
The second DROP procedure statement should be:
drop procedure if exists gen;

temporary table not returning result set in MySQL stored proc

I have a stored proc which inserts rows from a view with ranks into a temporary table.
The temp table is created before I run a cursor loop that inserted values, and SELECT'ed after the loop is done.
However when I CALL medianMessagesPerWeek(); I get an "Error Code : 1329 No data - zero rows fetched, selected, or processed."
If I create the table as a MYISAM table I can manually select the table and confirm that data has been inserted but the stored proc will still give me nothing.
Am I missing something here?
DELIMITER $$
USE `yongopal_metrics`$$
DROP PROCEDURE IF EXISTS `medianMessagesPerWeek`$$
CREATE DEFINER=`root`#`localhost` PROCEDURE `medianMessagesPerWeek`()
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE tempJoinWeek, tempActiveWeek, rank INT DEFAULT 0;
DECLARE joinWeek, activeWeek, memberNo, messages INT;
DECLARE cur CURSOR FOR SELECT * FROM cohortMessagesPerMemberPerWeek;
DROP TEMPORARY TABLE IF EXISTS medianMessagesPerWeek;
CREATE TEMPORARY TABLE medianMessagesPerWeek
(
joinWeek INT,
activeWeek INT,
memberNo INT,
messages INT,
rank INT
) ENGINE=MEMORY;
OPEN cur;
read_loop: LOOP
FETCH cur INTO joinWeek, activeWeek, memberNo, messages;
IF done THEN
LEAVE read_loop;
END IF;
IF tempJoinWeek = joinWeek AND tempActiveWeek = activeWeek THEN
SET rank = rank + 1;
ELSE
SET tempJoinWeek = joinWeek;
SET tempActiveWeek = activeWeek;
SET rank = 1;
END IF;
INSERT INTO medianMessagesPerWeek VALUES (joinWeek, activeWeek, memberNo, messages, rank);
END LOOP;
CLOSE cur;
SELECT * FROM medianMessagesPerWeek;
DROP TEMPORARY TABLE IF EXISTS medianMessagesPerWeek;
END$$
DELIMITER ;
EDIT
here is what cohortMessagesPerMemberPerWeek looks like
DELIMITER $$
USE `yongopal_metrics`$$
DROP VIEW IF EXISTS `cohortMessagesPerMemberPerWeek`$$
CREATE ALGORITHM=UNDEFINED DEFINER=`root`#`localhost` SQL SECURITY DEFINER VIEW `cohortMessagesPerMemberPerWeek` AS
SELECT
WEEK(`m`.`regDatetime`,0) AS `joinWeek`,
WEEK(`cd`.`sendDate`,0) AS `activeWeek`,
`m`.`memberNo` AS `memberNo`,
COUNT(0) AS `messages`
FROM (`yongopal`.`chatData` `cd`
JOIN `yongopal`.`members` `m`
ON ((`cd`.`sender` = `m`.`memberNo`)))
GROUP BY WEEK(`m`.`regDatetime`,0),WEEK(`cd`.`sendDate`,0),`m`.`memberNo`
ORDER BY WEEK(`m`.`regDatetime`,0),WEEK(`cd`.`sendDate`,0)$$
DELIMITER ;
Looks like you're missing a not found handler for your cur cursor. This is necessary to set your done boolean to true when the fetch statement no longer returns any rows (and hence you have reached the end of the dataset returned by the cursor declaration).
Give this a try:
DELIMITER $$
USE `yongopal_metrics`$$
DROP PROCEDURE IF EXISTS `medianMessagesPerWeek`$$
CREATE DEFINER=`root`#`localhost` PROCEDURE `medianMessagesPerWeek`()
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE tempJoinWeek, tempActiveWeek, rank INT DEFAULT 0;
DECLARE joinWeek, activeWeek, memberNo, messages INT;
DECLARE cur CURSOR FOR SELECT * FROM cohortMessagesPerMemberPerWeek;
declare continue handler for not found set done := true;
DROP TEMPORARY TABLE IF EXISTS medianMessagesPerWeek;
CREATE TEMPORARY TABLE medianMessagesPerWeek
(
joinWeek INT,
activeWeek INT,
memberNo INT,
messages INT,
rank INT
) ENGINE=MEMORY;
OPEN cur;
read_loop: LOOP
FETCH cur INTO joinWeek, activeWeek, memberNo, messages;
IF done THEN
LEAVE read_loop;
END IF;
IF tempJoinWeek = joinWeek AND tempActiveWeek = activeWeek THEN
SET rank = rank + 1;
ELSE
SET tempJoinWeek = joinWeek;
SET tempActiveWeek = activeWeek;
SET rank = 1;
END IF;
INSERT INTO medianMessagesPerWeek VALUES (joinWeek, activeWeek, memberNo, messages, rank);
END LOOP;
CLOSE cur;
SELECT * FROM medianMessagesPerWeek;
DROP TEMPORARY TABLE IF EXISTS medianMessagesPerWeek;
END$$
DELIMITER ;

For loop example in MySQL

In MySQL, I have this stored procedure with a LOOP:
DELIMITER $$
CREATE PROCEDURE ABC()
BEGIN
DECLARE a INT Default 0 ;
simple_loop: LOOP
SET a=a+1;
select a;
IF a=5 THEN
LEAVE simple_loop;
END IF;
END LOOP simple_loop;
END $$
It always prints 1. What is the correct syntax for a MySQL Loop?
drop table if exists foo;
create table foo
(
id int unsigned not null auto_increment primary key,
val smallint unsigned not null default 0
)
engine=innodb;
drop procedure if exists load_foo_test_data;
delimiter #
create procedure load_foo_test_data()
begin
declare v_max int unsigned default 1000;
declare v_counter int unsigned default 0;
truncate table foo;
start transaction;
while v_counter < v_max do
insert into foo (val) values ( floor(0 + (rand() * 65535)) );
set v_counter=v_counter+1;
end while;
commit;
end #
delimiter ;
call load_foo_test_data();
select * from foo order by id;
While loop syntax example in MySQL:
delimiter //
CREATE procedure yourdatabase.while_example()
wholeblock:BEGIN
declare str VARCHAR(255) default '';
declare x INT default 0;
SET x = 1;
WHILE x <= 5 DO
SET str = CONCAT(str,x,',');
SET x = x + 1;
END WHILE;
select str;
END//
Which prints:
mysql> call while_example();
+------------+
| str |
+------------+
| 1,2,3,4,5, |
+------------+
REPEAT loop syntax example in MySQL:
delimiter //
CREATE procedure yourdb.repeat_loop_example()
wholeblock:BEGIN
DECLARE x INT;
DECLARE str VARCHAR(255);
SET x = 5;
SET str = '';
REPEAT
SET str = CONCAT(str,x,',');
SET x = x - 1;
UNTIL x <= 0
END REPEAT;
SELECT str;
END//
Which prints:
mysql> call repeat_loop_example();
+------------+
| str |
+------------+
| 5,4,3,2,1, |
+------------+
FOR loop syntax example in MySQL:
delimiter //
CREATE procedure yourdatabase.for_loop_example()
wholeblock:BEGIN
DECLARE x INT;
DECLARE str VARCHAR(255);
SET x = -5;
SET str = '';
loop_label: LOOP
IF x > 0 THEN
LEAVE loop_label;
END IF;
SET str = CONCAT(str,x,',');
SET x = x + 1;
ITERATE loop_label;
END LOOP;
SELECT str;
END//
Which prints:
mysql> call for_loop_example();
+-------------------+
| str |
+-------------------+
| -5,-4,-3,-2,-1,0, |
+-------------------+
1 row in set (0.00 sec)
Do the tutorial: http://www.mysqltutorial.org/stored-procedures-loop.aspx
If I catch you pushing this kind of MySQL for-loop constructs into production, I'm going to shoot you with the foam missile launcher. You can use a pipe wrench to bang in a nail, but doing so makes you look silly.
Assume you have one table with name 'table1'. It contain one column 'col1' with varchar type. Query to crate table is give below
CREATE TABLE `table1` (
`col1` VARCHAR(50) NULL DEFAULT NULL
)
Now if you want to insert number from 1 to 50 in that table then use following stored procedure
DELIMITER $$
CREATE PROCEDURE ABC()
BEGIN
DECLARE a INT Default 1 ;
simple_loop: LOOP
insert into table1 values(a);
SET a=a+1;
IF a=51 THEN
LEAVE simple_loop;
END IF;
END LOOP simple_loop;
END $$
To call that stored procedure use
CALL `ABC`()
You can exchange this local variable for a global, it would be easier.
DROP PROCEDURE IF EXISTS ABC;
DELIMITER $$
CREATE PROCEDURE ABC()
BEGIN
SET #a = 0;
simple_loop: LOOP
SET #a=#a+1;
select #a;
IF #a=5 THEN
LEAVE simple_loop;
END IF;
END LOOP simple_loop;
END $$