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
Related
I'm trying the stored procedure below. However, when i pass the actual column name in 'DECLARE cur1' line, the SP returns correct value but when I pass variable name i.e. input parameter (colName), it returns 0. I've added comments in my code below. Is the code correct?
DROP PROCEDURE IF EXISTS test1.checkHardcodedField;
CREATE PROCEDURE test1.checkHardcodedField(IN textValue CHAR(10), colName CHAR(10), OUT counter VARCHAR(100))
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE temp CHAR(16);
DECLARE i INT DEFAULT 0;
-- Issue with this statement, returns incorrect value.
DECLARE cur1 CURSOR FOR SELECT colName FROM data1;
-- This statement works. MSH3 is actual column name
-- DECLARE cur1 CURSOR FOR SELECT MSH3 FROM data1;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN cur1;
read_loop: LOOP
FETCH cur1 INTO temp;
IF done THEN
LEAVE read_loop;
END IF;
IF temp = textValue THEN
SET i = i + 1;
END IF;
END LOOP;
SET counter = i;
CLOSE cur1;
END;
CREATE PROCEDURE procedure_name (..., fieldname VARCHAR, ...)
BEGIN
CREATE TEMPORARY TABLE tmp (field VARCHAR(10));
SET #sql := CONCAT('INSERT INTO tmp SELECT ', fieldname, ' FROM tablename;');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DROP PREPARE stmt;
BEGIN
DECLARE cur CURSOR FOR SELECT field FROM tmp;
...
END;
DROP TABLE tmp;
END
I have 2 schemas with exactly the same tables schema_oltp and schema_olap. I want to delete from selected tables in schema_oltp only the data which already exists in schema_olap. The tables I want to delete from are listed in a table called archived_to_delete. For each of the tables listed in archived_to_delete, I need to delete the data in batches of 100 000. This is what I have so far. It only deletes 100 000 in each table and stops. I want it to continue until there is no data in schema_oltp which also exists in schema_olap:
DELIMITER $$
CREATE DEFINER=`root`#`%` PROCEDURE `schema_oltp`.`schemaDeletes`(IN from_schema VARCHAR(100), IN to_schema VARCHAR(100))
BEGIN
DECLARE v_tname varchar(100);
DECLARE v_pkey varchar(455);
DECLARE v_upfield varchar(455);
DECLARE done INT DEFAULT 0;
DECLARE v_limit INT DEFAULT 100000;
DECLARE v_deleted INT;
DECLARE table_cursor CURSOR FOR SELECT a.table_name, a.column_name
FROM INFORMATION_SCHEMA.COLUMNS a
INNER JOIN archived_to_delete b
ON a.table_name = b.table_name
WHERE TABLE_SCHEMA = 'schema_oltp'
AND COLUMN_KEY='PRI'
ORDER BY b.archived_to_delete_id;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
OPEN table_cursor;
REPEAT
FETCH table_cursor INTO v_tname, v_pkey;
IF NOT done THEN
SET v_deleted=0;
SET #sql = CONCAT('DELETE FROM ',from_schema,'.', v_tname, ' WHERE ',v_pkey,' IN (SELECT ', v_pkey, ' FROM ',to_schema,'.', v_tname, ') limit ',v_limit,';');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET v_deleted=v_deleted+1;
IF v_deleted = v_limit THEN
commit;
SET v_deleted=0;
END IF;
SELECT CONCAT('Deleting...',v_tname);
END IF;
UNTIL done END REPEAT;
CLOSE table_cursor;
END$$
DELIMITER ;
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
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 am trying to find all the tables that has a column name called RecordID and then loop over those tables to see if the RecordID > 5000 or not.
CREATE PROCEDURE check_IDs ()
BEGIN
DECLARE done INT DEFAULT 0;
DECLARE result varchar(50);
DECLARE cur1 CURSOR for SELECT table_name FROM INFORMATION_SCHEMA.COLUMNS
WHERE column_name = 'RecordID' ;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
OPEN cur1;
read_loop: LOOP
FETCH cur1 INTO result;
IF done THEN
LEAVE read_loop;
END IF;
Select * from `result` where RecordID > 5000;
END LOOP;
close cur1;
END;
it says table result not found.
It's got to be
PREPARE stmt FROM CONCAT('SELECT * FROM `', result, '`WHERE RecordID > 5000;)';
EXECUTE stmt;
DEALLOCATE stmt;
instead of
Select * from `result` where RecordID > 5000;
see the examples (last one) of SQL Syntax for Prepared Statements
Your statement will use the hardcoded table name result. It seems, there's no such table in your database.