I think I'm narrowing in on my issue. I have a loop that is only firing once:
DELIMITER $$
DROP PROCEDURE IF EXISTS `thread_updates` $$
CREATE PROCEDURE `thread_updates`()
BEGIN
DECLARE done INT DEFAULT 0;
DECLARE my_curr_id INT DEFAULT NULL;
-- DECLARE other vars
DECLARE fixer_cursor CURSOR FOR
SELECT DISTINCT(id)
FROM log
WHERE date >= '2018-01-01';
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
OPEN fixer_cursor;
REPEAT
FETCH fixer_cursor INTO my_curr_id;
IF NOT done THEN
SELECT data FROM table WHERE id = my_curr_id; -- This may not exist
-- Do other stuff with 'data' or NULL from above
END IF;
UNTIL done END REPEAT;
CLOSE fixer_cursor;
END $$
DELIMITER ;
I think the issue may be that inside the IF NOT done THEN loop, I have a few select statements that may be trying to select results that don't exist (not found).
This is fine (for me) as the logic continues along using NULL values in those spots, but I suspect that my CONTINUE HANDLER FOR NOT FOUND is catching the NOT FOUND warning that these selects throw inside the loop and are thus stopping the entire loop prematurely.
How can I listen for NOT FOUND warning on my cursor only?
Or, how can I suppress the NOT FOUND warning in my MAYBE FOUND select statements inside my loop so my loop continues?
I think I have solved the issue by implementing a counter in a loop rather than relying on the NOT FOUND handler:
DELIMITER $$
DROP PROCEDURE IF EXISTS `thread_updates` $$
CREATE PROCEDURE `thread_updates`()
BEGIN
DECLARE my_total INT DEFAULT NULL; -- Declare total
DECLARE my_counter INT DEFAULT 0; -- Declare counter starting at 0
DECLARE my_curr_id INT DEFAULT NULL;
-- DECLARE other vars
DECLARE fixer_cursor CURSOR FOR
SELECT DISTINCT(id)
FROM log
WHERE date >= '2018-01-01';
OPEN fixer_cursor;
SELECT FOUND_ROWS() INTO my_total; -- Get total number of rows
my_fixerloop: LOOP
FETCH fixer_cursor INTO my_curr_id;
IF my_counter >= my_total THEN -- Compare counter to total
CLOSE fixer_cursor;
LEAVE my_fixerloop;
END IF;
SET my_counter = my_counter + 1; -- Increment by one for each record
SELECT data FROM table WHERE id = my_curr_id; -- This may not exist
-- Do other stuff with 'data' or NULL from above
END LOOP;
END $$
DELIMITER ;
Related
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.
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
;
I am working on a MySQL procedure that creates a summary of one of my tables. The procedure retrieves a dataset and loops through it using a cursor. For each iteration of the loop, the procedure updates another table. The problem I'm having is the loop ends after a single iteration, event though I know that the query I'm running retrieves more than one row.
BEGIN
# Variable declaration section omitted for brevity
DECLARE cur CURSOR FOR SELECT
t.result_id,
t.athlete_id, t.`first`, t.middle, `last`, t.pref, t.birth,t.uss,
t.club_id,t.code,t.club_name,
t.meet_name,t.meet_id,t.`start`,t.`end`,
MIN(t.time) as time,t.age,t.type
FROM sometable t GROUP BY club_id ORDER BY time asc,t.start desc,club_id;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
OPEN cur;
read_loop: LOOP
FETCH cur INTO result_id,athlete_id, first_name, middle, last_name, pref, birth,uss,
club_id,club_code,club_name,
meet_name,meet_id,start_date,end_date,
result_time,age,type;
IF done=1 THEN
LEAVE read_loop;
END IF;
SET last_time = result_time;
INSERT INTO toptimes(`result_id`,`club_id`,`agegroup`,`sex`,`distance`,`course`,`stroke`,`data`,`published`)
VALUES(result_id,club_id,AgeGroupID,sex,distance,course,stroke,json,0);
END LOOP read_loop;
CLOSE cur;
END
I'm not clear what the problem is. When I run the select query manually, I get back several rows. Is there a problem running an insert statement inside the loop?
Your code chunk looks good to me.
How do you know that it's running only one iteration (i'm not seeing any
print or select statement for debug purpose)?
Are you getting any error while executing the stored procedure?
I tried to replicate the similar code with "sakila" database (mysql sample db). It's working perfectly. Please check this sql code sample, if it helps you.
DROP PROCEDURE IF EXISTS usp_select_dummy_data ;
DELIMITER $$
CREATE DEFINER=`root`#`localhost` PROCEDURE usp_select_dummy_data()
BEGIN
-- Declare your variables
Declare _var_actor_id int default 0;
DECLARE _var_film_id int default 0;
-- Declare variable used for cursor and loop control
DECLARE done int;
DECLARE loop_counter INT DEFAULT 0;
-- Declare the cursor
DECLARE cur CURSOR FOR
SELECT
actor_id, film_id
FROM film_actor;
-- Declare handlers
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
-- Open the cursor
OPEN cur ;
-- Start the loop
read_loop: LOOP
FETCH cur
INTO _var_actor_id, _var_film_id ;
-- break out of the loop if
-- 1. if there is no rows or
-- 2. we've processed them all
IF done = 1 THEN
CLOSE cur ;
LEAVE read_loop ;
END IF;
-- Count the number of times looped
SET loop_counter = loop_counter + 1 ;
END LOOP read_loop ;
-- print the loop count
select loop_counter;
END
I´m new to InnoDB and starting with transactions. I´ve been 24 hours trying to get this to work.
I´m creating an exchange site and really need a transaction to be made. First, make a Select and find some data, and then some updates and inserts according with the results given.
I won´t post the full query as it might be very complicated to read so I created a new query to point out whats bothering.
Table Log
CREATE TABLE `log` (
`num_rows` int(10) unsigned NOT NULL,
`new_value` int(10) unsigned NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Stored Procedure
DROP PROCEDURE IF EXISTS `test`//
CREATE PROCEDURE `test` (IN var1 BIGINT)
BEGIN
DECLARE done INT DEFAULT 0;
DECLARE result INT;
DECLARE num_rows INT;
DECLARE cur1 CURSOR FOR
SELECT #var1 := #var1 +1 AS result;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
START TRANSACTION;
OPEN cur1;
SELECT FOUND_ROWS() into num_rows;
INSERT INTO log (num_rows,new_value) VALUES (num_rows,var1);
read_loop:
LOOP
FETCH cur1 INTO result;
IF done = 1 THEN
LEAVE read_loop;
END IF;
END LOOP read_loop;
CLOSE cur1;
COMMIT;
END//
When I try
CALL test(1);
Im passing 1 as var1 parameter. So in cur1, the value should be increased. And later insert a new row to the log with the new value. It looks like := asignment isn´t working.
I actually changed
SELECT #var1 := #var1 +1 AS result;
for this
SELECT var1 := var1 +1 AS result;
And get an error on ":= var1 +1"
I understand that the code of the question is an abstraction of the actual code of the stored procedure, so do not quite understand what you need to do, however, a code like this can be helpful.
/* Procedure structure for procedure `test` */
/*!50003 DROP PROCEDURE IF EXISTS `test` */;
DELIMITER $$
CREATE PROCEDURE `test`(IN `var1` BIGINT)
BEGIN
DECLARE `done` TINYINT(1) DEFAULT 0;
DECLARE `result` BIGINT;
DECLARE `_num_rows` INT;
DECLARE `cur1` CURSOR FOR
SELECT SQL_CALC_FOUND_ROWS #`var1` := `var1` + 1;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET `done` := 1;
START TRANSACTION;
OPEN `cur1`;
SET `var1` := #`var1`;
SELECT FOUND_ROWS() INTO `_num_rows`;
INSERT INTO `log` (`num_rows`, `new_value`) VALUES (`_num_rows`, `var1`);
`read_loop`: LOOP
FETCH `cur1` INTO `result`;
IF (`done`) THEN
LEAVE `read_loop`;
END IF;
END LOOP `read_loop`;
CLOSE `cur1`;
COMMIT;
END$$
DELIMITER ;
It is important to indicate the difference between 9.4. User-Defined Variables and routine parameters 13.1.15. CREATE PROCEDURE and CREATE FUNCTION Syntax, are different variables.
SQL Fiddle demo
I have written a stored proc in mysql when i am running it through hopper it is working fine but when i am trying to run it from mysql workbench or java it is not returning any result and also not showing any exception
I will request you to please help me on this
-- --------------------------------------------------------------------------------
-- Routine DDL
-- Note: comments before and after the routine body will not be stored by the server
-- --------------------------------------------------------------------------------
DELIMITER $$
CREATE DEFINER=`root`#`localhost` PROCEDURE `issueitem`()
BEGIN
DECLARE done INT DEFAULT 0;
DECLARE inventoryids INT DEFAULT 0;
DECLARE batch varchar(39);
DECLARE resultstr varchar(3000) DEFAULT '';
DECLARE exp DATE;
DECLARE mfgdate DATE;
DECLARE availableunit INT;
DECLARE quantity INT DEFAULT 100;
DECLARE oldest_date DATETIME;
DECLARE cur_count INT;
DECLARE que_size INT DEFAULT 0;
DECLARE curs CURSOR FOR SELECT inventoryid,batch,exp,availableunit FROM aashramdata.inventory where itemid=1 ORDER BY exp ASC;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
OPEN curs;
SET cur_count=quantity;
read_loop: LOOP
FETCH curs INTO inventoryids,batch,exp,availableunit;
IF done THEN
LEAVE read_loop;
END IF;
SET que_size = que_size + availableunit;
IF cur_count >= availableunit THEN
set cur_count=cur_count-availableunit;
set resultstr=CONCAT(resultstr,batch,' - ',exp,' - ',availableunit,' - ');
update aashramdata.inventory set `availableunit`=0 where inventoryid=inventoryids;
END IF;
IF cur_count < availableunit THEN
update aashramdata.inventory set `availableunit`=availableunit-cur_count where inventoryid=inventoryids;
set resultstr=CONCAT(resultstr,batch,' - ',exp,' - ',availableunit-cur_count,' - ');
set cur_count=0;
END IF;
IF que_size >= quantity then
LEAVE read_loop;
END IF;
END LOOP;
CLOSE curs;
select resultstr;
END
Since the procedure is apparently syntactically valid, it's impossible to answer what the problem might be, without some knowledge of the data.
The easiest way to debug a stored procedure is by peppering it with unbounded SELECT statements (that is, selects that are not part of a subquery and not part of an INSERT ... SELECT or SELECT ... INTO) and then running it from the MySQL command line client, which handles multiple result sets from stored procedures much more gracefully than most graphical clients.
For example:
...
SET cur_count=quantity;
SELECT cur_count; -- add this
read_loop: LOOP
FETCH curs INTO inventoryids,batch,exp,availableunit;
SELECT inventoryids,batch,exp,availableunit; -- add this
SELECT done; -- add this
IF done THEN
...
When run from the command line client...
mysql> CALL issueitem();
...output will start rolling out of the console at you, showing you the internal values the procedure is encountering as it iterates the loop.
This, or other SELECT statements like this added elsewhere, will expose the internal variables and this should help you find the problem. You'll need to remove them before you call the procedure from code or even the GUI since the GUI may not handle them well -- it may start opening new tabs or splitting panes or just ignoring everything after the first SELECT.
Note that the last iteration of the loop, you may see the values from the previous iteration repeated since the failed read from the cursor may not reset the variables, but "done" will also transition from 0 to 1 indicating that the cursor has run out of rows and fired the CONTINUE HANDLER, so those values won't actually have been processed twice.