MySQL Stored Procedure Cursor example - mysql

following code is running fine, what I need to ask how can I use a variable instead of a table name written in cursor select statement?
block5: begin
declare var1 int default 0;
DECLARE done TINYINT DEFAULT FALSE;
DECLARE cursor1 CURSOR FOR SELECT co_srno FROM vu_company where katscode in ('OGDC', 'LUCK', 'MCB');
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN cursor1;
my_loop: LOOP
FETCH cursor1 INTO var1;
IF done THEN
LEAVE my_loop;
ELSE
set #s = concat("insert into ", #temp_table_name_scrnr_data, " (sr_no) values (", var1, ")");
PREPARE stmt FROM #s;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END IF;
END LOOP;
end block5;

Related

MYSQL stored procedure: read value from one table and use it in WHERE on another

I'm trying to create a simple stored procedure that reads IDs from one table and deletes records that reference these IDs from another. I have a cursor which works fine, the problem is using that cursor in WHERE clause (for development purposes I'm using SELECT instead of DELETE):
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE id CHAR(30);
DECLARE cur1 CURSOR FOR SELECT idea_id FROM projects WHERE DATEDIFF(DATE(NOW()), DATE(last_update)) > 14;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN cur1;
read_loop: LOOP
FETCH cur1 INTO id;
IF done THEN
LEAVE read_loop;
END IF;
SET #A:= CONCAT('select * from stats where idea = "',id,'"');
Prepare stmt FROM #A;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END LOOP;
CLOSE cur1;
END
The problem is, that last select from the stats table always returns 0 results, even though the results are there if I manually get IDs from the cursor and paste them into the WHERE clause on stats. I suspect I'm not using the variable in the prepared statement correctly but I can't find a solution. Please help.
Nevermind, found the issue: the type of id declared in the procedure didn't match the type of idea_id field in the projects table and it was quietly truncated and then obviously no results matched. Please close.
I had the same issue as you, just set done to false after each loop
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE id CHAR(30);
DECLARE cur1 CURSOR FOR SELECT idea_id FROM projects WHERE DATEDIFF(DATE(NOW()), DATE(last_update)) > 14;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN cur1;
read_loop: LOOP
FETCH cur1 INTO id;
IF done THEN
LEAVE read_loop;
END IF;
SET #A:= CONCAT('select * from stats where idea = "',id,'"');
Prepare stmt FROM #A;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET done = FALSE;
END LOOP;
CLOSE cur1;
END

Can't specify table name on mysql stored procedure

I am mass assigning new id numbers to things in the DB to make room for some stuff at the beginning of each table. I created a procedure that works, but when I try adding input parameters to allow scripting, it can't find the table
delimiter |
CREATE PROCEDURE changeID
( IN in_table_name varchar(64))
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE a,b INT DEFAULT 800000;
DECLARE cur1 CURSOR FOR SELECT id FROM in_table_name ORDER BY id;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN cur1;
read_loop: LOOP
FETCH cur1 INTO b;
IF done THEN
LEAVE read_loop;
END IF;
UPDATE in_table_name SET id = a + 1 where id = b;
SET a = a+1;
END LOOP;
CLOSE cur1;
END;
|
delimiter ;
When I run this using call changeID('users'), I get the error:
[Err] 1146 - Table 'databaseName.in_table_name' doesn't exist
I was hoping to loop through using a simple list of commands like this so it could run unattended instead of manually changing the in_table_name between each execution:
call changeID('users');
call changeID('appointments');
call changeID('groups');
You can't dynamically pass a table name in a query, however, you can concatenate a string and then execute it as a statement. You of course want to be careful and ensure that this data has been sanitized etc. I wasn't able to test this, but something to this effect should get you going.
...
END IF;
SET #Query = CONCAT('UPDATE ',in_table_name,' SET `id` = ',a+1,' WHERE `id`=',b);
PREPARE stmt FROM #Query;
EXECUTE stmt;
...
https://dev.mysql.com/doc/refman/5.7/en/sql-syntax-prepared-statements.html
KChason got me started in the right direction, but I had to take it a little further to get the first part working from tips here: https://forums.mysql.com/read.php?98,138495,138908#msg-138908.
DROP PROCEDURE
IF EXISTS `workingversion`;
delimiter |
CREATE PROCEDURE `workingversion` (IN tableName VARCHAR(100))
BEGIN
DECLARE done INT DEFAULT 0 ;
DECLARE a,
b INT DEFAULT 800000 ;
DROP VIEW IF EXISTS v1;
SET #stmt_text = CONCAT("CREATE VIEW v1 AS SELECT id FROM ", tableName, " ORDER BY id") ;
PREPARE stmt
FROM
#stmt_text ; EXECUTE stmt ; DEALLOCATE PREPARE stmt ;
BEGIN
DECLARE cur1 CURSOR FOR SELECT * FROM v1 ;
DECLARE CONTINUE HANDLER FOR SQLSTATE '02000'
SET done = 1 ; OPEN cur1 ;
REPEAT
FETCH cur1 INTO b ;
IF NOT done THEN
SET #Query = CONCAT('UPDATE ',tableName,' SET `id` = ',a+1,' WHERE `id`=',b);
PREPARE stmt FROM #Query;
EXECUTE stmt;
SET a = a+1;
END
IF ; UNTIL done
END
REPEAT
; CLOSE cur1 ;
END ;
END

pass table name as parameter to cursor in mysql stored procedure

While implementing cursor I am unable to pass the table name as parameter to the select query declared in the cursor. How could I pass the table name as parameter to taht query ?
DELIMITER //
Drop PROCEDURE IF EXISTS GetRecord;
CREATE PROCEDURE GetRecord(IN hr int,IN TableName varchar(50))
BEGIN
declare done int DEFAULT 0;
declare ID int DEFAULT 0;
declare name varchar(25);
declare cur1 cursor for SELECT id ,name FROM TableName;
declare continue handler for not found set done=1;
SET #query = concat('update Student set no=no+? where id=?');
PREPARE stmt from #query;
open cur1;
igmLoop: loop
fetch cur1 into ID,name;
SELECT ID;
IF done = 1 THEN
LEAVE igmLoop;
END IF;
SET #id = ID;
SET #HR =hr;
EXECUTE stmt USING #HR,#id;
end loop igmLoop;
close cur1;
DEALLOCATE PREPARE stmt;
END;//
DELIMITER ;

MySQL Procedure Syntax Error "Missing END"

I have to following procedure trying to dynamically create a view.
CREATE DEFINER=`root`#`%` PROCEDURE `uspCreateViewFromTable`(IN ViewName varchar(255), IN TableName varchar(255))
BEGIN
#View Droppen falls sie schon erstellt wurde
SET #s = CONCAT('DROP VIEW IF EXISTS ',ViewName);
PREPARE stmt FROM #s;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
# Verwendete Spalten filtern und Statement bauen
#SET #columns = CAST('SELECT ' AS VARCHAR(10));
DECLARE column varchar(500);
DECLARE column_cursor FOR SELECT column_name FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = Tablename;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN column_cursor
read_loop: LOOP
FETCH column_cursor INTO column
# do something
SELECT column;
IF done THEN
LEAVE read_loop;
END IF;
END LOOP;
CLOSE column_cursor;
END
I get the Error "Missing END" and I have no idea why.
The syntax checker underlines the semikolon at the end of Line
DEALLOCATE PREPARE stmt;
When I move the dealloc to the end the syntac checker highlightes the semikolon at the line before.
EXECUTE stmt;
If I remove everthing after the dealloc it works.
Some problems:
DECLARE is permitted only inside a BEGIN ... END compound statement and must be at its start, before any other statements. See 13.6.3 DECLARE Syntax.
ERROR 1193 (HY000): Unknown system variable 'done'.
Check the syntax of cursors. See 13.6.6.2 Cursor DECLARE Syntax.
column is keyword and reserved word. See 9.3 Keywords and Reserved Words.
Missing some ;.
DELIMITER //
CREATE PROCEDURE `uspCreateViewFromTable`(
IN ViewName varchar(255),
IN TableName varchar(255)
)
BEGIN
/*
#View Droppen falls sie schon erstellt wurde
SET #s = CONCAT('DROP VIEW IF EXISTS ',ViewName);
PREPARE stmt FROM #s;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
*/
# Verwendete Spalten filtern und Statement bauen
#SET #columns = CAST('SELECT ' AS VARCHAR(10));
-- DECLARE column varchar(500);
DECLARE `column` varchar(500);
DECLARE done BOOL DEFAULT FALSE;
/*
DECLARE column_cursor FOR
SELECT column_name
FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_name = Tablename;
*/
DECLARE column_cursor CURSOR FOR
SELECT column_name
FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_name = Tablename;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
#View Droppen falls sie schon erstellt wurde
SET #s = CONCAT('DROP VIEW IF EXISTS ',ViewName);
PREPARE stmt FROM #s;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- OPEN column_cursor
OPEN column_cursor;
read_loop: LOOP
-- FETCH column_cursor INTO column
FETCH column_cursor INTO `column`;
# do something
-- SELECT column;
SELECT `column`;
IF done THEN
LEAVE read_loop;
END IF;
END LOOP;
CLOSE column_cursor;
END//
DELIMITER ;

Pass table name to select statement from cursor

How can I use values returned from Cursor as table names in mysql procedures ?
DECLARE cur CURSOR FOR
select table_name, column_name
from information_schema.columns
where table_schema = 'foo' and table_name like 'bar%';
OPEN cur;
loop1: LOOP
FETCH cur
INTO table_val, column_val;
IF no_more_rows THEN
CLOSE cur;
LEAVE loop1;
END IF;
update table_val SET column_val ...
This throws error that foo.table_val doesnt exist. How can I get the actual table name to be passed to the select statement ?
Change update table_val SET column_val ... into
SET #sql = CONCAT('UPDATE ', table_val, ' SET ', column_val, ' = whatever WHERE...');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
Read more about it here.
But note that you can not parameterize table and column names. This only works with values.
I will also share my fix. hope it will help someone too.
-- set to "$$" as we have ";" inside of Procedure. MySQL will be confused.(is this part of Procedure or should I run it now?)
DELIMITER $$
CREATE PROCEDURE UpdateTable()
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE _table_name CHAR(255);
DECLARE cur CURSOR FOR
SELECT table_name FROM information_schema.tables
WHERE table_schema = 'db_name' AND table_type = "BASE TABLE";
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN cur;
My_loop: LOOP
FETCH cur INTO _table_name;
SET #my_table_name = _table_name;
IF done THEN
LEAVE My_loop;
END IF;
SET FOREIGN_KEY_CHECKS = 0;
SET #stmt = CONCAT('ALTER TABLE ', #my_table_name, ' CONVERT TO CHARACTER SET utf8;');
PREPARE stmt1 FROM #stmt;
EXECUTE stmt1;
DEALLOCATE PREPARE stmt1;
SET FOREIGN_KEY_CHECKS = 1;
END LOOP;
CLOSE cur;
END$$
-- set to normal. ";"
DELIMITER ;