Nested Cursors in MySQL Procedures - mysql

I need to iterate through a particular table for some conditions.On that conditions it will return a list of row data,which i need to delete in the procedure,but the data is linked with multiple other tables so what I thought to do is to get the primary key of the fetched rows in one cursor and concatenate it with comma in my first iteration and with that concatenated list I can do one query to fetch datas from linked tables (using FIND_IN_SET function)...
I tried something like this which is not working as expected...Can anyone please tell me whether this can be possible to do or to do in anyother way...
Here in this example,
There are two tables scmn_sop_dtls and scmn_sop_addnl_dtls,these two tables have foreign key relation with one column so,what i'm trying to do is i'll fetch some details from scmn_sop_dtls and insert into record table and the one i'm inserting i need to delete in scmn_sop_dtls table.So when i delete it is throwing foreign key relation error since it is linked with scmn_sop_addnl_dtls.So first i need to collect all the sopid and concatenated into list and that list i'm passing to the second cursor to fetch that particular row from child table(scmn_sop_addnl_dtls). but it is not fetching them.
DELIMITER $$
USE `scmn_nov21`$$
DROP PROCEDURE IF EXISTS `MoveSampleData`$$
CREATE DEFINER=`root`#`localhost` PROCEDURE `MoveSampleData`()
BEGIN
Block1: BEGIN
DECLARE done INT DEFAULT 0;
DECLARE par_sop_id VARCHAR(50);
DECLARE par_sop_addnl_id VARCHAR(50);
DECLARE sopdl_sop_id_list VARCHAR(500);
DECLARE sopdl_sop_param_list VARCHAR(500);
DECLARE sopdl_temp_sop_param_list VARCHAR(500);
DECLARE sopdl_temp_sop_id VARCHAR(10);
DECLARE c_1 CURSOR FOR SELECT sop_id FROM scmn_sop_dtls WHERE created_date < NOW() - INTERVAL 1 MONTH;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
OPEN c_1;
REPEAT
FETCH c_1 INTO par_sop_id;
INSERT INTO record VALUES(par_sop_id,"Sub","Sop data","NIL",0,NOW());
SET sopdl_temp_sop_id = CONCAT(par_sop_id, ',');
SET sopdl_sop_id_list = CONCAT(sopdl_sop_id_list, sopdl_temp_sop_id);
Block2: BEGIN
DECLARE done2 INT DEFAULT 0;
DECLARE c_2 CURSOR FOR SELECT sop_addnl_id FROM scmn_sop_addnl_dtls WHERE FIND_IN_SET(sop_id,sopdl_sop_id_list);
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done2 = 1;
OPEN c_2;
REPEAT
FETCH c_2 INTO par_sop_addnl_id;
INSERT INTO record_arc VALUES(par_sop_addnl_id,"Sub","Sop data","NIL",0,NOW());
UNTIL done2 END REPEAT;
CLOSE c_2;
END Block2;
UNTIL done END REPEAT;
CLOSE c_1;
END Block1;
END$$
DELIMITER ;

Related

Stored procedure to fetch entire row from database

I am a beginner to MYSQL and currently practicing stored procedures. I am trying to create a procedure that should fetch a row of an entire field when given an input parameter. Is there any workaround for this? If yes, then it would be an immense help.
Many Thanks
Welcome to the Forum.
A stored procedure can simple consist of a SELECT statement that returns all the columns in the row you want. e.g.
CREATE PROCEDURE `myproc`(IN `p_id` INT(11))
READS SQL DATA
SELECT id, COL2, COL3, ETC
FROM `mytable`
WHERE id = 'p_id';
If you need to traverse a table, looking at every row that meets some criteria, then you need to DECLARE a "cursor", and use that to fetch the rows you want. Here is a template I use to create new procedures that need to traverse a table - it can be adapted to suit your needs. In this template I nly retrieve two columns from the cursor, but you can retrieve any number. You have to declare a local variable for each column in your SELECT so the FETCH statement has somewhere to put the data it retrieves from the cursor:
CREATE PROCEDURE `traverse_table`()
READS SQL DATA
BEGIN
DECLARE var_id INT(11) UNSIGNED;
DECLARE var_data varchar(16383);
DECLARE done INT DEFAULT FALSE;
DECLARE ecode varchar(1000) ;
DECLARE emsg varchar(1000) ;
DECLARE nextrecord CURSOR FOR # here is the CURSOR DECLARATION
SELECT `myid`,`mydata` # It will return these rows, one
FROM `mytable` # at a time
WHERE `mydata` LIKE "ABC%";
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
BEGIN
GET DIAGNOSTICS CONDITION 1 ecode = RETURNED_SQLSTATE, emsg = MESSAGE_TEXT;
SELECT var_id,var_data,ecode,emsg;
END;
OPEN nextrecord;
FETCH nextrecord into var_id,var_data; # here is the first Fetch
WHILE (done = FALSE) DO
# >>>>>> do something with var_id,var_data here <<<<<<
SET done = FALSE;
FETCH NEXT FROM records into var_id,var_data; # here is the second and subsequent Fetch
END WHILE;
CLOSE nextrecord;
END

Why MySQL Cursor stopped looping while reporting a success

I created a procedure as below in MySQL workbench 8.0.12 community, win10. A cursor is declared to loop through more than 16000 member's ID in a table to insert their number of orders created in different dates into another table.
Member's IDs and their dates of registration are selected here for the cursor. select distinct BAG,AAA from DICT_ALLE_AAAF where AAA>=Date_Memb_Star. Once I call it call PRO_MIDD_MEMB_AAAA_1('2017/01/01','2019/11/10',60), it only looped through 5 member's IDs and did not report any error. But if I took the while loop out of the procedure and with select Var_BAG on, the cursor loop went selecting more than 200 member's IDs until it asked me to cancel it. Can anyone tell me what is wrong in my code? Many thanks.
delimiter //
create procedure PRO_MIDD_MEMB_AAAA_1
(in Date_Memb_Star date,
in Date_Sale_End date,
in Int_Inte int)
begin
declare Int_Floo int default 0;
declare Int_Ceil int;
declare Int_MembAll int;
declare Int_Loop int default 1;
declare Int_MaxiLoop int;
declare Int_DaySpen int default 30;
declare Int_Done int;
declare Var_BAG varchar(16);
declare Date_Memb_Regi date;
declare Cur_Memb cursor for
select distinct BAG,AAA from DICT_ALLE_AAAF where AAA>=Date_Memb_Star order by BAG;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET Int_Done = 1;
truncate table MIDD_MEMB_AAAA;
open Cur_Memb;
read_loop:loop
fetch Cur_Memb into Var_BAG,Date_Memb_Regi;
if Int_Done=1 then
leave read_loop;
end if;
/*select Var_BAG;*/
set Int_MaxiLoop=ceil(datediff(Date_Sale_End,Date_Memb_Regi)/Int_Inte);
set Int_Floo=0;
while Int_Loop<=Int_MaxiLoop do
set Int_Ceil=Int_Floo+Int_Inte;
insert into MIDD_MEMB_AAAA
select Var_BAG,Int_Floo,Int_Ceil,
count(distinct BAK)*(Int_DaySpen/Int_Inte) as Numb_Con_Avg
from OPER_SALE_AAAB
where BAG=Var_BAG
and timestampdiff(hour,Date_Memb_Regi,AAA)/24>Int_Floo
and timestampdiff(hour,Date_Memb_Regi,AAA)/24<=Int_Ceil
and AAB>0;
set Int_Floo=Int_Floo+Int_Inte;
set Int_Loop=Int_Loop+1;
end while;
end loop read_loop;
close Cur_Memb;
end//
delimiter ;
Little oversight: you forgot to reset your variable Int_Loop, so after the first run, it only enters the loop when Int_MaxiLoop has a new maximum value.
Reinitialize it (to 1, I assume) before the loop:
...
set Int_Floo=0;
set Int_Loop=1;
while Int_Loop<=Int_MaxiLoop do ...

Can't use Stored Procedure parameter in WHERE clause

I'm having a lot of troubles with one situation on my DB.
I'm trying to code a Stored Procedure that inserts an entry to a log table for audit purposes after a SELECT is done in a specific table (the idea is that it should work similar to how a AFTER SELECT Trigger would work). The Stored Procedure has one input parameter, the WHERE condition/clause.
The user executes the SP and writes the condition (for example IDCultura=1). The SP uses that parameter to make a SELECT statement like this: SELECT * FROM dba.medicoes WHERE *IDCultura=1*
My problem arrives when I try to make a cursor that loops the results from that query, so it inserts one line in the log table for each result of the SELECT.
I can't use the parameter as WHERE clause, but if I manually write the same text in the clause, it works.
I've seen some solutions that use a CONCAT to join all the parts of the query before execution. But because I'm using the SELECT query when declaring the cursor, I can't SET a variable before.
Here's the code I'm working with right now:
CREATE DEFINER=`root`#`localhost` PROCEDURE `select_medicoes`(
whereCondicao varchar(200))
BEGIN
DECLARE ID_novo int;
DECLARE ID_Variavel int;
DECLARE ID_Cultura int;
DECLARE NumMed int;
DECLARE DataHoraMed date;
DECLARE ValorMed int;
DECLARE done INT DEFAULT FALSE;
DECLARE curs_medicoeslog cursor for
(SELECT *
FROM `dba`.medicoes
WHERE `whereCondicao`);
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
SELECT `whereCondicao`;
OPEN curs_medicoeslog;
read_loop: LOOP
FETCH curs_medicoeslog INTO ID_Variavel, ID_Cultura, NumMed, DataHoraMed, ValorMed;
INSERT INTO log_medicoes (IDVariavel, IDCultura, NumMedicao, DataHoraMedicao, ValorMedicao, Utilizador, `Data`, Operacao)
VALUES (ID_Variavel, ID_Cultura, NumMed, DataHoraMed, ValorMed, current_user(), now(), 'S');
IF done THEN
LEAVE read_loop;
END IF;
END LOOP;
CLOSE curs_medicoeslog;
END

Handling unfound data in mySQL procedure loop

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 ;

mysql stored procedure variable error

Hi I am finding my way round MySQL trying to migrate data from MSsql for the first time. I have created a stored procedure that clear all the tables down so I can import the data repeatedly as I test and alter things. I thought I had this pretty well figured but I am unsure how to get the actual value of the variable to go with the delete statement. Thanks for any assistance
DELIMITER $$
CREATE PROCEDURE ClearData ()
BEGIN
DECLARE v_finished INTEGER DEFAULT 0;
DECLARE v_TableName varchar(100) DEFAULT "";
-- declare cursor for list of tables to clear
DEClARE TableToRemove CURSOR FOR
SELECT TABLE_NAME from information_schema.tables where table_type ='BASE TABLE';
-- declare NOT FOUND handler
DECLARE CONTINUE HANDLER
FOR NOT FOUND SET v_finished = 1;
OPEN TableToRemove;
ClearData: LOOP
FETCH TableToRemove INTO v_TableName;
IF v_finished = 1 THEN
LEAVE ClearData;
END IF;
--This is the problem how do I get the value of v_TableName
delete from `v_TableName`;
END LOOP ClearData;
CLOSE TableToRemove;
END$$
DELIMITER ;