7

I'm looking for a straight way to run a query on all databases hosted on my mysql server.

I have a bunch of Magento installations and I want to truncate all Magento log table on all databases:

  • log_customer
  • log_visitor
  • log_visitor_info
  • log_url
  • log_url_info
  • log_quote
  • report_viewed_product_index
  • report_compared_product_index
  • report_event
  • catalog_compare_item

I think it something very easy to accomplish in mysql but I cannot find a straight answer/solution.

*UPDATE *
According to @Ollie Jones it is not possible to do it without a STORE PROCEDURE or a server side language ( PHP or whatever )

UPDATE 1
I choose to follow the PHP approach (@samitha) for 2 reasons:

  1. STORE PROCEDURE looks more complicated
  2. Query on 'information_schema' table is very slow ( at least if you have many DB/TABLES)
WonderLand
  • 4,853
  • 6
  • 50
  • 71

4 Answers4

13
  SELECT DISTINCT SCHEMA_NAME AS `database`
    FROM information_schema.SCHEMATA
   WHERE  SCHEMA_NAME NOT IN ('information_schema', 'performance_schema', 'mysql')
   ORDER BY SCHEMA_NAME

gets you a list of all the non-MYSQL databases on your system.

  SELECT TABLE_SCHEMA AS `database`,
         TABLE_NAME AS `table`
    FROM information_schema.TABLES
   WHERE TABLE_TYPE = 'BASE TABLE'
   ORDER BY TABLE_SCHEMA, TABLE_NAME

gets you a list of all the actual tables (excluding SYSTEM VIEWs like the TABLES table, and user-defined views) in all the databases.

Then, you should implement logic in your program to ensure that, for each database, it really is a Magento database before you truncate certain tables. Otherwise, you might become a despised person among your co-workers. :-)

Edit

Here's a stored procedure.

You need to edit it to do exactly what you need it to do; in particular, it counts rows rather than truncating tables, and it doesn't contain the correct list of log tables. (It would be irresponsible for me to publish such a wildly destructive stored procedure; you should edit it yourself to do the destructive part.)

DELIMITER $$
DROP PROCEDURE IF EXISTS `zap_magento_logs`$$

CREATE PROCEDURE `zap_magento_logs`()
BEGIN

    -- declare variables for database and table names
    DECLARE dbname VARCHAR(128) DEFAULT '';
    DECLARE tbname VARCHAR(128) DEFAULT '';

    DECLARE done INTEGER DEFAULT 0;

    -- declare cursor for list of log tables
    DECLARE log_table_list CURSOR FOR 
      SELECT TABLE_SCHEMA AS `database`,
             TABLE_NAME AS `table`
        FROM `information_schema`.TABLES
       WHERE TABLE_TYPE = 'BASE TABLE'
         AND TABLE_NAME IN 
         (
            'log_customer',
        'log_visitor',
        'log_visitor_info',
        'log_url',
        'log_url_info',
        'log_quote'
         )
       ORDER BY TABLE_SCHEMA, TABLE_NAME;

    -- declare NOT FOUND handler
        DECLARE CONTINUE HANDLER 
        FOR NOT FOUND SET done = 1;

    OPEN log_table_list;

    log_table: LOOP

        FETCH log_table_list INTO dbname, tbname;

        IF done = 1 THEN
        LEAVE log_table;
        END IF;

        -- create an appropriate text string for a DDL or other SQL statement
        SET @s = CONCAT('SELECT COUNT(*) AS num FROM  ',dbname,'.',tbname);
        PREPARE stmt FROM @s;
        EXECUTE stmt;
        DEALLOCATE PREPARE stmt; 
    END LOOP    log_table;
    CLOSE log_table_list;

END$$

DELIMITER ;

You run this by issuing the SQL command

  CALL zap_magento_logs();
O. Jones
  • 81,279
  • 15
  • 96
  • 133
  • can you expand your answer ... what should I do at this point ? how to loop on all the result a truncate the wanted tables ? it is my local developed environment :) – WonderLand Dec 28 '13 at 17:19
  • #1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'database FROM information_schema.SCHEMATA ORDER BY SCHEMA_NAME LIMIT 0, ' at line 1 – underscore Dec 28 '13 at 17:19
  • @WonderLand Anything you write may make it into production! – O. Jones Dec 28 '13 at 17:20
  • @samitha oops, forgot to escape reserved words. Fixed. Thanks. – O. Jones Dec 28 '13 at 17:22
  • @OllieJones is there any way to unselected certain databases using onlysql ? – underscore Dec 28 '13 at 17:24
  • To do this in pure MySQL without the help of a host language is going to require a stored procedure. This is because DDL (such as TRUNCATE) can't be mixed with DML outside stored procedures. – O. Jones Dec 28 '13 at 17:24
  • @samitha what do you mean "unselect certain databases" ? – O. Jones Dec 28 '13 at 17:25
  • Thanks for the explanation, my mysql knowledge is not very rich :) if it is not a big deal please share the store procedure approach – WonderLand Dec 28 '13 at 17:26
  • Ex: i have 7 databases in my server.I don't want to select 4 of them.Can only using mysql? – underscore Dec 28 '13 at 17:26
  • what about something similar to this one: http://stackoverflow.com/questions/1575419/how-can-i-truncate-multiple-tables-in-mysql – WonderLand Dec 28 '13 at 17:36
  • 1
    Well, OK, here is a stored procedure. I hesitate to give you this because it contains multiple levels of MySQL concepts with which you may not be familiar, and I am not a believer in simply teaching people "magic spells." – O. Jones Dec 28 '13 at 18:28
6

A PHP approach would be:

$tables = array(
    'log_customer',
    'log_visitor',
    'log_visitor_info',
    'log_url',
    'log_url_info',
    'log_quote',
    'report_viewed_product_index',
    'report_compared_product_index',
    'report_event',
    'catalog_compare_item',
);

$dbh = new PDO('mysql:host=localhost;', 'USERNAME', 'PASSWORD', array(
    PDO::ATTR_PERSISTENT => true
));


$sql = $dbh->query('SHOW DATABASES');
$getAllDbs = $sql->fetchALL(PDO::FETCH_ASSOC);

foreach ($getAllDbs as $DB) {  

        foreach ($tables as $table) {
            $dbh->query('TRUNCATE TABLE ' . $DB['Database'] . '.' . $table);

    };
};
WonderLand
  • 4,853
  • 6
  • 50
  • 71
underscore
  • 5,787
  • 4
  • 30
  • 72
4

I didn't feel like writing code to solve this so I found a different solution. I wrote SQL that generates the SQL that I need. So I saved the following to a file called createSomeSQL.sql:

SET sql_mode='PIPES_AS_CONCAT';

select 
'truncate table ' || dbs.database || '.someLogTable;'
 as ''
from  (SELECT DISTINCT SCHEMA_NAME AS `database`
         FROM information_schema.SCHEMATA
        WHERE SCHEMA_NAME NOT IN ('information_schema', 'performance_schema', 'mysql', 'test')
        ORDER BY SCHEMA_NAME) as dbs;

You could replace the SQL in line 4 with anything you want. Then I ran this command to generate the SQL that I need:

mysql -u root -p < createSomeSQL.sql > sqlToExecute.sql

Replace "root" with your username, of course. Now the file sqlToExecute.sql contains a script you can run to execute that SQL against all your databases.

Ryan Shillington
  • 15,463
  • 10
  • 75
  • 85
  • 1
    I think this is the simplest answer and the best one! – Budianto IP Dec 18 '17 at 08:28
  • 1
    We have an identical database for each client. Using Ryan's script as a basis, we added user accounts with privileges using ```SELECT CONCAT("GRANT DELETE,INSERT,SELECT,UPDATE ON `",TABLE_SCHEMA, "`.* TO 'someuser'@'%';") FROM information_schema.TABLES WHERE TABLE_TYPE = 'BASE TABLE' AND TABLE_NAME = 'a_table_in_all_similar_databases' ORDER BY TABLE_SCHEMA ;``` – PeteW Jan 02 '18 at 13:52
1

Try the following (very basic, no error handling, may not work at all, I've not tested this):

$db = mysqli_connect(); // your database connection

$tables = ["log_customer", "log_visitor", "log_visitor_info"]; // array with all the tables

foreach ($tables as $table) {
    mysqli_query($db, "TRUNCATE TABLE `".$table."`"); // executes query for each element in the array
}