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
Related
I am writing a procedure to convert data from multiple smaller tables into a 'flat' table with many columns(~100).
I have a cursor defined which reads/joins data from smaller tables.
So far I have seen that 'fetch' semantics works with explicit variables in mysql. Is there a way where I can do something like: 'fetch * from cursor; followed by insert'?
Following is a sample (for single field insert).
How to deal if let's say temp and jsontable had 50 columns. (fetching into 50 explicit variables or is there a better way?)
[Oracle has something called 'BULK COLLECT']
CREATE PROCEDURE simpleproc_insert (OUT param1 INT)
BEGIN
DECLARE done INT DEFAULT FALSE;
declare iden int;
declare msg JSON;
declare insertCursor cursor for select * from temp;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
open insertCursor;
read_loop: LOOP
FETCH insertCursor INTO iden,msg;
IF done THEN
LEAVE read_loop;
END IF;
insert into jsontable(jdoc) values(msg);
END LOOP;
close insertCursor;
END
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
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 just want to return multiple row using CURSOR of procedure. But It return empty value. I've used a simple select query for test purpose.
CREATE DEFINER=`root`#`localhost` PROCEDURE `get_user`()
READS SQL DATA
BEGIN
DECLARE id INT;
DECLARE name VARCHAR (256);
DECLARE done int default 0;
DECLARE curl CURSOR FOR
SELECT id, name FROM user;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET DONE = 1;
OPEN curl;
data_loop:LOOP
FETCH curl INTO id, name;
IF done=1 THEN
leave data_loop;
END IF;
END LOOP data_loop;
CLOSE curl;
END
A procedure must terminated (about multiple rows) with a SELECT.
But I can't show your final SELECT.
For example, you can use a temporary table USERS2 (CREATE TEMPORARY TABLE users2) with the same fields of USER table. In the loop you can write in USERS2 (INSERT INTO users2 ... and so on), so at the end you'll write:
SELECT * FROM users2
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;