Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Running the same query on multiple databases in one query (MySQL)

Tags:

database

mysql

I have multiple databases with the same structure but different unrelated names, and I want to run this query on all of them:

SELECT FROM <dbname>.`cms_users` WHERE `email` LIKE '%admin.something%'

I searched a bit in the information_schema and found a table called SCHEMATA that contains the databases' names which is ultimately what I need to run the query above and I can do so manually by just replacing the database name myself and creating the query. However I want to do it automatically using a mysql loop but I'm not very certain how can I do this and how can I concatenate the database name to the query and run it.

My pseudo code for this would be as follows:

array dbnames= select `SCHEMA_NAME` from `information_schema`.`SCHEMATA`;

loop start on dbnames
SELECT FROM dbnames[index].`cms_users` WHERE `email` LIKE '%admin.bilsi%';
loop end

Any help to put this or a better logic into mysql syntax? Thanks.

like image 613
Naguib Ihab Avatar asked Dec 15 '22 17:12

Naguib Ihab


1 Answers

SQL Server and other RDBMS products allow you do scripting in the console. You can use anything you can use in stored procedures. MySQL is unfortunately much more limited and does not allow flow control constructs outside of stored procedures and functions. That means no loops, no if-statements, no cursors. You can use variables, but only the ones that start with @.

Furthermore, if you do a loop, you'll be sending multiple result-sets back to the client. If you're just running queries from a console, this is fine. If the results are something you intend for a program to use, this may not be desirable (it may not be desirable in either case).

If you are doing this in a one-off sort of way, get a list of databases manually, and then use copy and paste to build a query using UNION ALL, like so:

SELECT FROM `first_db`.`cms_users` WHERE `email` LIKE '%admin.bilsi%'
UNION ALL
SELECT FROM `second_db`.`cms_users` WHERE `email` LIKE '%admin.bilsi%'
UNION ALL
SELECT FROM `third_db`.`cms_users` WHERE `email` LIKE '%admin.bilsi%';

If you expect the number of databases to be changing and you don't want to have to update your query, or you are sending it from a program, you can use dynamic SQL. This means building a query in a string variable and then submitting it using MySQL's prepared statement functionality.

On the console, you can use something like this (see: http://dev.mysql.com/doc/refman/5.0/en/sql-syntax-prepared-statements.html):

SELECT GROUP_CONCAT(CONCAT("SELECT FROM `", SCHEMA_NAME, "`.`cms_users` WHERE `email` LIKE '%admin.bilsi%'") SEPARATOR ' UNION ALL ')
  INTO @stmt_sql 
  FROM INFORMATION_SCHEMA.SCHEMATA 
 WHERE SCHEMA_NAME NOT IN('mysql', 'test', 'tmp', 'information_schema', 'sys', 'performance_schema');
PREPARE stmt FROM @stmt_sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

This generates the query I showed above using information from the INFORMATION_SCHEMA pseudo-database, namely, the list of databases by name (which MySQL incorrectly calls schemas). The rest is just the boilerplate code needed to prepare and execute a prepared statement, as per the linked documentation.

There are other ways, but they are even more tedious and won't buy you much.

like image 142
siride Avatar answered May 09 '23 16:05

siride