I have a server with multiple schemas. I need to find specyfic email address. Every schema looks pretty much te same, they have same structure. What i wanted to do is to write a script to for displaying dbname and specyfic data related to given email . Heres my script, but it do not work:(
DELIMITER $$
#Drop procedure if exists SearchAllDb $$
Create procedure SearchAllDb()
BEGIN
DECLARE DB_NAME Varchar(50);
DECLARE done INT default FALSE;
DECLARE CURSOR_ALL_DB_NAMES CURSOR FOR
SELECT schema_name from information_schema.schemata
WHERE schema_name like 'itools_%';
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN CURSOR_ALL_DB_NAMES;
myloop: LOOP
FETCH CURSOR_ALL_DB_NAMES INTO DB_NAME;
IF done THEN LEAVE myloop;
END IF;
WHILE done != TRUE DO
SET #SQL = CONCAT('select DB_NAME, id, sn, name, email
from',DB_NAME,'.`open_cases` where email like 'xxx#yahoo.com'');
prepare stmt from #SQL;
execute stmt;
deallocate prepare stmt;
END WHILE;
CLOSE CURSOR_ALL_DB_NAMES;
END;
DELIMITER ;
I have something similar written in t-sql and it works...
DECLARE #DB_NAME VARCHAR(50);
DECLARE CURSOR_ALL_DB_NAMES CURSOR FOR
SELECT name FROM master.sys.databases
WHERE name like 'pickup-%' and name!= 'pickup-stored';
OPEN CURSOR_ALL_DB_NAMES;
FETCH NEXT FROM CURSOR_ALL_DB_NAMES INTO #DB_NAME
WHILE ##Fetch_Status = 0
BEGIN
Print #DB_NAME;
exec ('select '''+ #DB_NAME + ''' as db, id, sn, email from ['+
#DB_NAME+'].dbo.requests where email in (''xxx#live.com'')');
FETCH NEXT FROM CURSOR_ALL_DB_NAMES INTO #DB_NAME
END
CLOSE CURSOR_ALL_DB_NAMES;
DEALLOCATE CURSOR_ALL_DB_NAMES;
Looks like you were doing fine up until you introduced the WHILE.
THE WHILE is redundant since you are using a LOOP, you should remove those lines.
The LOOP doesn't have an END.
The CONCAT statement will treat the first use of DB_NAME as one of your column names and there are some quoting issues with the LIKE.
The ';' at the end of the procedure should be '$$' which you have specified as your DELIMITER.
I've taken a few liberties with your variable names but this tidied up version should illustrate the points.
DELIMITER $$
DROP PROCEDURE IF EXISTS SearchAllDb $$
CREATE PROCEDURE SearchAllDb()
BEGIN
DECLARE db_name VARCHAR(64);
DECLARE done INT DEFAULT FALSE;
DECLARE cur CURSOR FOR
SELECT schema_name
FROM information_schema.schemata
WHERE schema_name LIKE 'itools_%';
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN cur;
myloop: LOOP
FETCH cur INTO db_name;
IF done THEN
LEAVE myloop;
END IF;
SET #SQL = CONCAT('SELECT ''', db_name, ''', id, sn, name, email
FROM `', db_name,'`.`open_cases`
WHERE email LIKE ''xxx#yahoo.com''');
PREPARE stmt FROM #SQL;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END LOOP;
CLOSE cur;
END $$
DELIMITER ;
There's a very good CURSOR example in the documentation at https://dev.mysql.com/doc/refman/5.7/en/cursors.html which will help you with the structure of the procedure.
Related
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
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
I wanted to use table names and run a statement with the table name as variable. I used cursor/fetch but when I run a statement with the variable it is not using the value of the variable but just seems to use the variable_name itself. I have seen example with concat where another variable was defined but what if I just wanted to reference the table name in a COMMAND?
DELIMITER $$
DROP PROCEDURE IF EXISTS test $$
CREATE PROCEDURE test()
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE v_table_name TEXT;
DECLARE cur1 CURSOR FOR
SELECT TABLE_NAME FROM information_schema.tables where table_schema = 'rt';
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done= TRUE;
OPEN cur1;
myloop: loop
FETCH cur1 INTO v_table_name;
IF done THEN
LEAVE myloop;
END IF;
COMMAND table v_table_name;
END loop;
close cur1;
END $$
If by COMMAND you mean you want to use the value of a variable as an identifier in another SQL statement... you may be able to make use of a prepared statement (in the context of a MySQL stored program).
Reference: https://dev.mysql.com/doc/refman/5.6/en/sql-syntax-prepared-statements.html
As a trivial example of what that might look like:
SET #sql = CONCAT('select * from `',v_table_name,'` limit 1');
PREPARE stmt1 FROM #sql;
EXECUTE stmt1;
DEALLOCATE PREPARE stmt1;
SET #sql = NULL;
Note that this approach is not safe from SQL Injection vulnerabilities.
If that's not what you are looking for, I'm at a loss. I don't understand what you are referring to as a COMMAND.
I know this query for changing the charset and collation of mysql table.
alter table <some_table> convert to character set utf8 collate utf8_unicode_ci;
But I need a query for changing all the tables in a db. Is there any possible solutons.
I have written this procedure to execute statements for every table in a database:
DROP PROCEDURE IF EXISTS sp_forEveryTable;
DELIMITER $$
CREATE PROCEDURE sp_forEveryTable(IN p_schema varchar(50), IN p_stmt varchar(100))
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE v_tbl varchar(50);
DECLARE cur CURSOR FOR SELECT table_name FROM information_schema.tables WHERE table_schema = p_schema;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN cur;
read_loop: LOOP
FETCH cur INTO v_tbl;
IF done THEN
LEAVE read_loop;
END IF;
SET #sql := REPLACE(p_stmt, '?', v_tbl);
IF (UPPER(p_stmt) LIKE 'SELECT %') THEN
SET #sql := CONCAT('SELECT "', v_tbl, '", ', SUBSTRING(#sql FROM 7));
ELSE
SELECT v_tbl AS 'Execute statement for following table:';
END IF;
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END LOOP;
CLOSE cur;
END $$
DELIMITER ;
Use it like this:
CALL sp_forEveryTable('your_database_name', 'ALTER TABLE ? CONVERT TO CHARACTER SET utf8 COLLATE utf8_unicode_ci');
To have the tables created in the future in this database have the utf8 character set and collation as default use the statement given in this answer.
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 ;