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
Related
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
Scenario: I have a stored procedure that gets data from a table based on 2 inputs: a date and a string (which is a column name). The first procedure is called from another procedure which uses a cursor to loop through rows of a table and pass each row to the string of the first procedure (column names to be checked). My input for the second procedure (which is the one to be called directly) is the date.
Issue: My first procedure is running fine when I call it on its own. My second procedure is throwing some syntax errors that I don't know how to fix.
Obs: I already check some other answers here on this topic
such as Using Cursor in a Loop of a stored procedure and How can I loop through all rows of a table? (MySQL) . Actually my second procedure is now a modified version of a query I found on SE https://dba.stackexchange.com/questions/138549/mysql-loop-through-a-table-running-a-stored-procedure-on-each-entry
Issue: Currently, the code is throwing an error at line 5, in my declare of #colval.
Code:
-- Procedure for looping through rows of `wanted_columns` table:
delimiter $$
drop procedure if exists `data_check_loop` $$
create procedure `data_check_loop`(`wanted_date` date)
begin
set #dateval = `wanted_date`;
declare colval string default null;
-- boolean variable to indicate cursor is out of data
declare done tinyint default false;
-- declare a cursor to select the desired columns from the desired source table
declare cursor1
cursor for
select t1.c1
from `wanted_columns` t1;
-- catch exceptions
declare continue handler for not found set done = true;
-- open the cursor
open cursor1;
my_loop:
loop
fetch next from cursor1 into colval;
if done then
leave my_loop;
else
call `set_column_stats`(colval, dateval);
end if;
end loop;
close cursor1;
end $$
delimiter ;
Question: Any ideas on how to fix this?
You have a couple of problems in your procedure. Firstly, as described in the manual:
DECLARE is permitted only inside a BEGIN ... END compound statement and must be at its start, before any other statements.
So you need to move your
set #dateval = `wanted_date`;
after all the DECLAREs (including the cursor and continue handler).
Secondly, your declaration of colval is incorrect, string is not a valid data type and should be replaced with text:
declare colval text default null;
I want to create a Cursor in a stored procedure from mysql. The question is, it's giving a error when the Cursor is created after the variable initialization. Is there any solution for it.
DELIMITER //
Create procedure sp_JB_Securities_Second_row_Insert()
BEGIN
DECLARE Rw_Count int;
set Rw_Count = (select count(*) from JB_Security_Detials):
DECLARE cur1 CURSOR FOR select title from JB_Security_Detials limit 27, Rw_Count;
END //
DELIMITER ;
It has been pointed out by others (P.Salmon) that the order for DECLARING variables and the CURSOR is significant and it is very specific.
e.g.
Variable declarations must appear before cursor or handler
declarations.
and
Cursor declarations must appear before handler declarations and after
variable and condition declarations.
However, switching the order around is not going to help you here because you are looking to use RW_Count in your LIMIT clause and setting the variable before the CURSOR declaration is causing the error. This restriction also means that the only way you could use a variable in the LIMIT clause is if you pass it in as a parameter to the procedure (which you probably don't want to do).
Fortunately, none of that necessary as you don't really need to know the number of rows in the table to use OFFSET with LIMIT.
There's a nice example in the documentation for the SELECT statement
To retrieve all rows from a certain offset up to the end of the result
set, you can use some large number for the second parameter. This
statement retrieves all rows from the 96th row to the last:
SELECT * FROM tbl LIMIT 95,18446744073709551615;
So, the solution here is to just remove the RW_Count variable completely and add a very BIG number instead.
try this
Create procedure sp_JB_Securities_Second_row_Insert()
BEGIN
DECLARE cur1 CURSOR FOR select title from JB_Security_Detials limit
27,(select count(*) from JB_Security_Detials);
END //
DELIMITER ;
Try this
DELIMITER //
DROP PROCEDURE IF EXISTS sp_JB_Securities_Second_row_Insert()
CREATE PROCEDURE sp_JB_Securities_Second_row_Insert()
BEGIN
DECLARE Rw_Count INT;
DECLARE exit_loop BOOLEAN;
SET Rw_Count = (SELECT COUNT(*) FROM JB_Security_Detials);
DECLARE Rw_Count_cursor CURSOR FOR
"Your Query .............. "
DECLARE CONTINUE HANDLER FOR NOT FOUND SET exit_loop = TRUE;
OPEN Rw_Count_cursor;
-- start looping
out_loop: LOOP
-- read the name from next row into the variables
FETCH Rw_Count_cursor INTO col1,cl2, ...;
"Your Query .............. "
IF exit_loop THEN
CLOSE Rw_Count_cursor;
LEAVE out_loop;
END IF;
END LOOP out_loop;
END//
DELIMITER ;
I am curious how to reference an existing stored procedure SELECT statement from a secondary query or SET call within the same stored procedure.
For example:
CREATE PROCEDURE 'mysp' (OUT sumvalue INT)
BEGIN
-- This is the core recordset the SP returns
SELECT * FROM Table
-- I also want to return a value based on the above recordset
SET sumvale = SUM(previousselect.values)
END
Essentially, I have a SP that returns a detailed recordset, and I need to return SUM and custom values based on the data within that recordset. The issue is I cannot figure out how to reference the data after the SELECT statement (e.g. does it create an internal reference I can use such as #recordset1.X).
Any help would be appreciated.
Try using cursor from this link:
As MySql does not allow you to return a recordset from either store procedures or functions, you could try this:
CREATE DEFINER=`root`#`localhost` PROCEDURE `some_procedure`(out some_id int)
BEGIN
declare done boolean default false;
declare id int;
declare tot decimal(10,2);
declare some_cursor cursor for
select id, total from some_table where id = some_id;
declare continue handler for not found set done = true;
open some_cursor;
loop1: loop
fetch some_cursor into id, tot;
if done=true then
leave loop1;
end if;
//do your calculation here or whatever necessary you want to do with the code
end loop loop1;
END;
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.