Querying multiple databases at once - mysql

I have WordPress instances with each in its own database. For an update I need to query all active plugins, that are stored in a table 'wp_options' and accessible via
WHERE option_name='active_plugins'
How can I access all active plugin settings (spread over multiple databases) and output them in one single SQL result? I know the database.tablename syntax, but how do I go on from there with the above Where statement?
A request in a single database would look like this:
SELECT option_value
FROM `database`.`wp_options`
WHERE option_name="active_plugins"

SELECT option_value
FROM `database1`.`wp_options`
WHERE option_name="active_plugins"
UNION
SELECT option_value
FROM `database2`.`wp_options`
WHERE option_name="active_plugins"

The solution by Pentium10 is good but its drawback is that you have to extend the query for every schema to be included. The below solution uses a prepared statement to produce a result set for all schemas on your MySQL server which have the wp_options table. This should be more convenient for you.
DROP PROCEDURE IF EXISTS `MultipleSchemaQuery`;
DELIMITER $$
CREATE PROCEDURE `MultipleSchemaQuery`()
BEGIN
declare scName varchar(250);
declare q varchar(2000);
DROP TABLE IF EXISTS ResultSet;
create temporary table ResultSet (
option_value varchar(200)
);
DROP TABLE IF EXISTS MySchemaNames;
create temporary table MySchemaNames (
schemaName varchar(250)
);
insert into MySchemaNames
SELECT distinct
TABLE_SCHEMA as SchemaName
FROM
`information_schema`.`TABLES`
where
TABLE_NAME = 'wp_options';
label1:
LOOP
set scName = (select schemaName from MySchemaNames limit 1);
set #q = concat('select option_value from ', scName, '.wp_options where option_name=\'active_plugins\'');
PREPARE stmt1 FROM #q;
EXECUTE stmt1;
DEALLOCATE PREPARE stmt1;
delete from MySchemaNames where schemaName = scName;
IF ((select count(*) from MySchemaNames) > 0) THEN
ITERATE label1;
END IF;
LEAVE label1;
END LOOP label1;
SELECT * FROM ResultSet;
DROP TABLE IF EXISTS MySchemaNames;
DROP TABLE IF EXISTS ResultSet;
END
$$
DELIMITER ;
CALL MultipleSchemaQuery();

Gruber's answer works great, but it has a syntax error --- there's a spurious comma at the end of line 10. Here is the code, with syntax error fixed:
DELIMITER $$
CREATE PROCEDURE `MultipleSchemaQuery`()
BEGIN
declare scName varchar(250);
declare q varchar(2000);
DROP TABLE IF EXISTS ResultSet;
create temporary table ResultSet (
option_value varchar(200)
);
DROP TABLE IF EXISTS MySchemaNames;
create temporary table MySchemaNames (
schemaName varchar(250)
);
insert into MySchemaNames
SELECT distinct
TABLE_SCHEMA as SchemaName
FROM
`information_schema`.`TABLES`
where
TABLE_NAME = 'wp_options';
label1:
LOOP
set scName = (select schemaName from MySchemaNames limit 1);
set #q = concat('select option_value from ', scName, '.wp_options where option_name=\'active_plugins\'');
PREPARE stmt1 FROM #q;
EXECUTE stmt1;
DEALLOCATE PREPARE stmt1;
delete from MySchemaNames where schemaName = scName;
IF ((select count(*) from MySchemaNames) > 0) THEN
ITERATE label1;
END IF;
LEAVE label1;
END LOOP label1;
SELECT * FROM ResultSet;
DROP TABLE IF EXISTS MySchemaNames;
DROP TABLE IF EXISTS ResultSet;
END
$$

Yet another example of querying multiple databases using procedure, cursor, union all and prepared statement. Does not require drop and delete permissions:
USE `my_db`;
DROP PROCEDURE IF EXISTS `CountAll`;
DELIMITER $$
CREATE PROCEDURE `CountAll`(IN tableName VARCHAR(255))
BEGIN
DECLARE db_name VARCHAR(250);
DECLARE exit_loop BOOLEAN;
DECLARE union_query TEXT DEFAULT '';
DECLARE my_databases CURSOR FOR
SELECT DISTINCT `table_schema`
FROM `information_schema`.`tables`
WHERE
`table_schema` LIKE 'myprefix\_%' AND
`table_name` = tableName;
DECLARE CONTINUE HANDLER
FOR NOT FOUND SET exit_loop = TRUE;
OPEN my_databases;
get_database: LOOP
FETCH my_databases INTO db_name;
IF exit_loop THEN
-- remove trailing UNION ALL statement
SET union_query = TRIM(TRAILING ' UNION ALL ' FROM union_query);
LEAVE get_database;
END IF;
SET union_query = concat(union_query, 'SELECT COUNT(*) AS qty FROM ',
db_name, '.', tableName, ' UNION ALL ');
END LOOP get_database;
CLOSE my_databases;
SET #final_query = concat('SELECT SUM(qty) FROM (', union_query,
') AS total;');
PREPARE stmt1 FROM #final_query;
EXECUTE stmt1;
DEALLOCATE PREPARE stmt1;
END$$
DELIMITER ;
CALL CountAll('wp_options');

Related

Extract data from multiple tables (mysql)

My problem: I have a few tables in mysql database with column "URL". I want to extract all of URLs to text file or another table.
Is it good way to go?
DELIMITER $$
CREATE or replace PROCEDURE link()
BEGIN
DECLARE finished INTEGER DEFAULT 0;
DECLARE tablename varchar(1000) DEFAULT "";
DECLARE link_tables
CURSOR for
SELECT table_name FROM information_schema.columns WHERE table_name like '__baza%' and COLUMN_NAME like 'url';
DECLARE CONTINUE HANDLER
FOR NOT FOUND SET finished = 1;
OPEN link_tables;
getTable: LOOP
fetch link_tables into tablename;
IF finished = 1 THEN s
LEAVE getTable;
END IF;
select url from tablename into ... <<<< is it good idea? what to do next?
END LOOP getTable;
close link_tables;
END$$
DELIMITER ;
CALL link();
CREATE PROCEDURE link_urls (prefix VARCHAR(64))
BEGIN
SELECT GROUP_CONCAT( CONCAT( 'SELECT url FROM ', table_name) SEPARATOR ' UNION ')
INTO #sql
FROM information_schema.columns
WHERE table_name like CONCAT(prefix, '%');
SET #sql = CONCAT( 'CREATE TABLE urls ', #sql );
PREPARE stmt FROM #sql;
EXECUTE stmt;
DROP PREPARE stmt;
END
fiddle

how to delete all foreign keys for a table

I am new to writing procedures , I have checked some of the stackoverflow posts but there is no post found to delete all foreign keys for a specific table.
I have a written below procedure.
drop procedure if exists remove_foreign_key;
delimiter ;;
create procedure remove_foreign_key(vTableName varchar(128)) begin
if exists (select table_name from information_schema.columns where table_schema=database() and table_name = vTableName) then
(SELECT concat('ALTER TABLE ', TABLE_NAME, ' DROP FOREIGN KEY ', CONSTRAINT_NAME, ';')
FROM information_schema.key_column_usage
WHERE table_schema=database() and TABLE_NAME = vTableName
AND referenced_table_name IS NOT NULL);
end if;
end if;
end;;
delimiter ;
call remove_foreign_key('my_table_name');
above query return me all queries(number of foreign keys that table contains) which needs to be executed, but I want to execute them one by one.I have read about cursor but not sure about its implementation.can someone help?
below is the mysql procedure to delete the foreign keys of some specific table's, hope it helps.you have to pass your table name in call and booom , your foreign keys are deleted for those tables.
DROP PROCEDURE IF EXISTS remove_foreign_key;
DELIMITER ;;
CREATE PROCEDURE remove_foreign_key(vTableName varchar(128))
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE cName VARCHAR(64);
DECLARE cur CURSOR FOR
SELECT DISTINCT CONSTRAINT_NAME
FROM INFORMATION_SCHEMA.Key_COLUMN_USAGE
WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME = vTableName
AND REFERENCED_TABLE_NAME IS NOT NULL;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
SET AUTOCOMMIT=0;
SET FOREIGN_KEY_CHECKS=0;
OPEN cur;
read_loop: LOOP
FETCH cur INTO cName;
IF done THEN
LEAVE read_loop;
END IF;
SET #sql = CONCAT('ALTER TABLE ',vTableName,' DROP FOREIGN KEY ',cName,';');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END LOOP;
CLOSE cur;
SET FOREIGN_KEY_CHECKS=1;
COMMIT;
SET AUTOCOMMIT=1;
END ;;
DELIMITER ;
call remove_foreign_key('table_name1');
call remove_foreign_key('table_name2');

To compare two mysql database add missing tables and column using procedure

I have written a mysql procedure to compare two databases and add missing tables and columns but it show error i counld not spot the error i that procedure pls help me out to clear that error
DELIMITER $$
CREATE PROCEDURE database_comparison(IN original_db varchar(255),IN compare_db varchar(255))
BEGIN
DECLARE table_name_o varchar(255);
DECLARE column_name_o varchar(255);
DECLARE cursor_sts int default 0;
DROP TABLE IF EXISTS temp_db.original_database_list;
create temporary table temp_db.original_database_list SELECT TABLE_NAME,COLUMN_NAME,'table not exists' as flag_t,'column not exists' as flag_c FROM information_schema.`COLUMNS` where TABLE_SCHEMA=original_db and TABLE_NAME not like 'z%' and TABLE_NAME not like '%bk%';
DROP TABLE IF EXISTS temp_db.compare_database_list;
create temporary table temp_db.compare_database_list SELECT TABLE_NAME,COLUMN_NAME,'table not exists' as flag_t,'column not exists' as flag_c FROM information_schema.`COLUMNS` where TABLE_SCHEMA=compare_db and TABLE_NAME not like 'z%' and TABLE_NAME not like '%bk%';
update temp_db.original_database_list a join temp_db.compare_database_list b on a.TABLE_NAME=b.TABLE_NAME set a.flag_t='',b.flag_t='';
update temp_db.original_database_list a join temp_db.compare_database_list b on a.TABLE_NAME=b.TABLE_NAME and a.COLUMN_NAME=b.COLUMN_NAME set a.flag_c='',b.flag_c='';
DECLARE table_cursor CURSOR FOR select distinct TABLE_NAME from temp_db.original_database_list where flag_t<>'';
DECLARE CONTINUE HANDLER FOR NOT FOUND SET cursor_sts=1;
OPEN table_cursor;
table_loop:LOOP
IF cursor_sts=1 THEN
SET cursor_sts=0;
LEAVE table_loop;
END IF;
FETCH table_cursor INTO table_name_o;
create table compare_db.table_name_o like original_db.table_name_o;
END LOOP table_loop;
CLOSE table_cursor;
SET table_name_o='';
DECLARE column_cursor CURSOR FOR select distinct TABLE_NAME,COLUMN_NAME from temp_db.original_database_list where flag_t='' and flag_c<>'';
DECLARE CONTINUE HANDLER FOR NOT FOUND SET cursor_sts=1;
OPEN column_cursor;
column_loop:LOOP
IF cursor_sts=1 THEN
LEAVE table_loop;
END IF;
FETCH column_cursor INTO table_name_o,column_name_o;
SET #query =(SELECT concat('alter table `',compare_db,'`.`',table_name_o,'` add `',column_name_o,'` ',column_type,' not null;') FROM information_schema.`COLUMNS` where TABLE_NAME=table_name_o and a.COLUMN_NAME=column_name_o and TABLE_SCHEMA=original_db);
PREPARE stmt FROM #query;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END LOOP column_loop;
CLOSE column_cursor;
END $$
DELIMITER ;
**this is the corrected procedure it works**
DELIMITER $$
DROP PROCEDURE IF EXISTS database_comparison$$
CREATE PROCEDURE database_comparison(IN original_db varchar(255),IN compare_db varchar(255))
BEGIN
DECLARE table_name_o varchar(255);
DECLARE column_name_o varchar(255);
DECLARE cursor_sts int default 0;
DEClARE table_cursor CURSOR FOR
select distinct TABLE_NAME from temp_db.original_database_list where flag_t<>'';
DECLARE column_cursor CURSOR FOR
select distinct TABLE_NAME,COLUMN_NAME from temp_db.original_database_list where flag_t='' and flag_c<>'';
DECLARE CONTINUE HANDLER
FOR NOT FOUND SET cursor_sts=1;
DROP TABLE IF EXISTS temp_db.original_database_list;
create table temp_db.original_database_list SELECT TABLE_NAME,COLUMN_NAME,'table not exists' as flag_t,'column not exists' as flag_c FROM information_schema.`COLUMNS` where TABLE_SCHEMA=original_db and TABLE_NAME not like 'z%' and TABLE_NAME not like '%bk%';
DROP TABLE IF EXISTS temp_db.compare_database_list;
create table temp_db.compare_database_list SELECT TABLE_NAME,COLUMN_NAME,'table not exists' as flag_t,'column not exists' as flag_c FROM information_schema.`COLUMNS` where TABLE_SCHEMA=compare_db and TABLE_NAME not like 'z%' and TABLE_NAME not like '%bk%';
ALTER TABLE temp_db.original_database_list ADD index(TABLE_NAME,COLUMN_NAME);
ALTER TABLE temp_db.compare_database_list ADD index(TABLE_NAME,COLUMN_NAME);
update temp_db.original_database_list a join temp_db.compare_database_list b on a.TABLE_NAME=b.TABLE_NAME set a.flag_t='',b.flag_t='';
update temp_db.original_database_list a join temp_db.compare_database_list b on a.TABLE_NAME=b.TABLE_NAME and a.COLUMN_NAME=b.COLUMN_NAME set a.flag_c='',b.flag_c='';
OPEN table_cursor;
table_loop:LOOP
FETCH table_cursor INTO table_name_o;
IF cursor_sts=1 THEN
SET cursor_sts=0;
LEAVE table_loop;
END IF;
SET #query1 =concat('create table ',compare_db,'.',table_name_o,' like ',original_db,'.',table_name_o);
PREPARE stmt1 FROM #query1;
EXECUTE stmt1;
DEALLOCATE PREPARE stmt1;
END LOOP table_loop;
CLOSE table_cursor;
SET table_name_o='';
OPEN column_cursor;
column_loop:LOOP
FETCH column_cursor INTO table_name_o,column_name_o;
IF cursor_sts=1 THEN
LEAVE column_loop;
END IF;
SET #query =(SELECT concat('alter table `',compare_db,'`.`',table_name_o,'` add `',column_name_o,'` ',column_type,' not null;') FROM information_schema.`COLUMNS` where TABLE_NAME=table_name_o and COLUMN_NAME=column_name_o and TABLE_SCHEMA=original_db);
PREPARE stmt FROM #query;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END LOOP column_loop;
CLOSE column_cursor;
END $$
DELIMITER ;

Stored procedure to loop through a list of tables deleting data in batches of 100000 in MySQL

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 ;

mysql related: how to choose all option_name = 'template' in all multisite sites?

I am trying this command to choose all wp_%_options tables in all sites:
SELECT * FROM (SELECT * FROM information_schema.tables WHERE table_name LIKE 'wp_%_options') as t
But I need to get one level deeper and get the option_name = 'template' in all of these folders.
Tried this:
SELECT * FROM (SELECT * FROM information_schema.tables WHERE table_name like 'wp_%_options') as t WHERE option_name='template'
Not working.
Any ideas?
You can not treat table name (or column name) as variable in your standard SELECT queries. To solve the task you have to write a stored procedure.
The stored procedure should:
Fetch all table names wp_%_options;
Walk through the fetched names and select from the second level what you need using PREPARE statement.
DELIMITER |
DROP PROCEDURE IF EXISTS get_my_tables;
CREATE PROCEDURE get_my_tables (IN option_name VARCHAR(255))
BEGIN
DECLARE table_name CHAR(255);
DECLARE done INT DEFAULT FALSE;
DECLARE first INT DEFAULT TRUE;
DECLARE cur1 CURSOR FOR SELECT `tables`.`TABLE_NAME` FROM `information_schema`.`tables` `tables` WHERE `tables`.`TABLE_NAME` LIKE 'wp_%_options';
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN cur1;
read_loop: LOOP
FETCH cur1 INTO table_name;
IF done THEN
LEAVE read_loop;
END IF;
IF (first) THEN
SET first = FALSE;
DROP TABLE IF EXISTS `result_table`;
SET #sql = CONCAT("CREATE TEMPORARY TABLE `result_table` (SELECT * FROM `", table_name ,"` WHERE `option_name` = '", option_name, "')");
ELSE
SET #sql = CONCAT("INSERT INTO `result_table` SELECT * FROM `", table_name, "` WHERE `option_name` = '", option_name, "'");
END IF;
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END LOOP;
CLOSE cur1;
SELECT * FROM `result_table`;
END;
|
DELIMITER ;
CALL get_my_tables('template');
I tested this example now it works as expected. You should tune some logic for your needs. For example: how to work with temporary table, fetch fesult and maybe use UNION instead of temporary table.
This is what I did in the end:
UPDATE wp_2_options SET option_value='classic' WHERE option_name='template' OR option_name='stylesheet';
Then I created as many as I need in Excel. Took me 1 min to change them all.
Try using LIKE instead of =.
So that the second query will be:
SELECT * FROM (SELECT * FROM information_schema.tables WHERE table_name like 'wp_%_options') as t WHERE option_name LIKE 'template'