I am trying to cycle through all rows in a table that holds the database name for another database. This is what I have which compiles but doesn't work as MySQL takes the 'database' bit as the actual database name rather than the contents.
How can I change the following so that it takes the contents of the variable?
DROP PROCEDURE IF EXISTS CYCLE;
DELIMITER ;;
CREATE PROCEDURE CYCLE()
BEGIN
DECLARE data_name CHAR(255);
DECLARE done INT DEFAULT FALSE;
DECLARE cursor_i CURSOR FOR SELECT database FROM company;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN cursor_i;
read_loop: LOOP
FETCH cursor_i INTO database;
IF done THEN
LEAVE read_loop;
END IF;
SELECT * FROM database.table LIMIT 1;
END LOOP;
CLOSE cursor_i;
END;
;;
DELIMITER ;
Update: to be clear I know that 'database' is a reserved word. I have used that here in place of the actual variable name I have used.
You can't evaluate the content on a variable on a table select statement, in that case you must build a query in a string an execute:
DROP PROCEDURE IF EXISTS CYCLE;
DELIMITER ;;
CREATE PROCEDURE CYCLE()
BEGIN
DECLARE data_name CHAR(255); -- unused?
DECLARE done INT DEFAULT FALSE;
DECLARE cursor_i CURSOR FOR SELECT database_name FROM company;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN cursor_i;
read_loop: LOOP
FETCH cursor_i INTO data_name;
IF done THEN
LEAVE read_loop;
END IF;
-- now build the query before execute:
set #sql = concat('SELECT * FROM `',data_name,'`.`your_table` LIMIT 1');
-- and execute
prepare stmt from #sql;
execute stmt;
deallocate prepare stmt ;
END LOOP;
CLOSE cursor_i;
END;
;;
DELIMITER ;
*Would better not confusing us using variable names that causes more questions. #JustSaying
Related
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
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
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 ;
I'm trying to populate a table with values from an EXECUTE statement in a Stored Procedure. Is there a way to perform this or something similar.
DELIMITER $$
DROP PROCEDURE IF EXISTS `mysql`.`ShowUserGrants` $$
CREATE PROCEDURE mysql.ShowUserGrants()
BEGIN
DECLARE done BOOLEAN DEFAULT FALSE;
DECLARE strGrant CHAR(100);
DECLARE cur1 CURSOR
FOR SELECT CONCAT("SHOW GRANTS FOR '",user,"'#'",host,"';")
FROM mysql.user
WHERE host!='localhost';
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = true;
OPEN cur1;
read_loop:LOOP
FETCH cur1 INTO strGrant;
IF done THEN
CLOSE Cur1;
LEAVE read_loop;
END IF;
SET #a = strGrant;
PREPARE strStmt FROM #a;
INSERT INTO user_grants (grants) VALUES(EXECUTE strStmt);
/* EXECUTE strStmt; */
DEALLOCATE PREPARE strStmt;
END LOOP;
END $$
DELIMITER ;
The EXECUTE statement works as I want but need to populate the table user_grants.
Thanks.
DELIMITER $$
DROP PROCEDURE IF EXISTS `mysql`.`ShowUserGrants` $$
CREATE PROCEDURE mysql.ShowUserGrants()
BEGIN
DECLARE done BOOLEAN DEFAULT FALSE;
DECLARE strGrant CHAR(100);
DECLARE cur1 CURSOR
FOR SELECT CONCAT("SHOW GRANTS FOR '",user,"'#'",host,"';")
FROM mysql.user
WHERE host!='localhost';
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = true;
OPEN cur1;
read_loop:LOOP
FETCH cur1 INTO strGrant;
IF done THEN
CLOSE Cur1;
LEAVE read_loop;
END IF;
SET #a = 'INSERT INTO user_grants(grants) VALUES(?)';
PREPARE strStmt FROM #a;
SET #var = strGrant;
EXECUTE strStmt USING #var;
DEALLOCATE PREPARE strStmt;
END LOOP;
END $$
DELIMITER ;
or better, replace sp body with
INSERT INTO user_grants(grants)
SELECT CONCAT("SHOW GRANTS FOR '",user,"'#'",host,"';")
FROM mysql.user
WHERE host!='localhost';
SOLVED:
This works:
DROP PROCEDURE IF EXISTS drop_all_tables_from_specified_database;
DELIMITER $$
CREATE PROCEDURE drop_all_tables_from_specified_database(IN dbname CHAR(50))
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE tblname CHAR(50);
DECLARE cur1 CURSOR FOR SELECT table_name FROM information_schema.tables WHERE table_schema = dbname COLLATE utf8_general_ci;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
SET foreign_key_checks = 0;
OPEN cur1;
read_loop: LOOP
FETCH cur1 INTO tblname;
IF done THEN
LEAVE read_loop;
END IF;
SET #database_name = dbname;
SET #table_name = tblname;
SET #sql_text = CONCAT('DROP TABLE ', #database_name, '.' , #table_name, ';');
PREPARE stmt FROM #sql_text;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END LOOP;
CLOSE cur1;
SET foreign_key_checks = 1;
END$$
DELIMITER ;
CALL drop_all_tables_from_specified_database('test');
DROP PROCEDURE drop_all_tables_from_specified_database;
I am trying to create a procedure that deletes all tables inside a database. I have saved this code inside a file tear-down.sql:
CREATE PROCEDURE drop_all_tables_from_specified_database()
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE table_to_drop VARCHAR(255);
DECLARE cur1 CURSOR FOR SELECT table_name FROM information_schema.tables WHERE table_schema='test';
OPEN cur1;
read_loop: LOOP
FETCH cur1 INTO table_to_drop;
IF done:
LEAVE read_loop;
END IF;
DROP TABLE `test`.table_to_drop;
END LOOP;
CLOSE cur1;
END;
When I then do inside MySQL command line tool:
mysql> source tear-down.sql
I get a massive error:
I don't understand the error I get and I cannot figure out what's wrong. Anybody can spot an error in this procedure?
EDIT:
My current code:
DROP PROCEDURE IF EXISTS drop_all_tables_from_specified_database;
DELIMITER $$
CREATE PROCEDURE drop_all_tables_from_specified_database(IN dbname CHAR(50))
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE tblname CHAR(50);
DECLARE cur1 CURSOR FOR SELECT table_name FROM information_schema.tables WHERE table_schema = dbname;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN cur1;
read_loop: LOOP
FETCH cur1 INTO tblname;
IF done THEN
LEAVE read_loop;
END IF;
DROP TABLE dbname.tblname;
END LOOP;
CLOSE cur1;
END$$
DELIMITER ;
CALL drop_all_tables_from_specified_database('test');
DROP PROCEDURE drop_all_tables_from_specified_database;
Now it runs ok but it doesn't delete the tables. I get this response:
It looks like you will need to set an alternative DELIMITER:
DELIMITER $$
CREATE PROCEDURE drop_all_tables_from_specified_database()
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE table_to_drop VARCHAR(255);
DECLARE cur1 CURSOR FOR SELECT table_name FROM information_schema.tables WHERE table_schema='test';
OPEN cur1;
read_loop: LOOP
FETCH cur1 INTO table_to_drop;
/* This might need to be 'IF done <> FALSE THEN' */
IF done THEN
LEAVE read_loop;
END IF;
DROP TABLE `test`.table_to_drop;
END LOOP;
CLOSE cur1;
END$$
DELIMITER ;
It should be IF done THEN not IF done: . Also done variable does not get updated so you might get an infinite loop in there. Take a look at fetch cursor into.