Conditional change of database engine - mysql

I would like to change the table format of my database to InnoDB. But I only want the action to run if the table format is not InnoDB yet.
If you execute the following command, a time-intensive action is started again and again (similar to a repair), if the table format is already InnoDB:
ALTER TABLE `MyTable` ENGINE InnoDB;
Is there a condition, which you can insert for this case, so that the operation runs faster, if the target format is already InnoDB?
I'm thinking about something like:
ALTER TABLE `MyTable` ENGINE InnoDB WHERE ENGINE != 'InnoDB';
ALTER TABLE IGNORE `MyTable` ENGINE InnoDB;

You can use information_schema.TABLES, to generate the script:
set #db_schema = 'test';
set #alter_query = 'alter table `{table}` engine=innodb;';
select group_concat(replace(
#alter_query,
'{table}',
replace(t.TABLE_NAME, '`', '``')
) separator '\n') as script
from information_schema.TABLES t
where t.TABLE_SCHEMA = #db_schema
and t.ENGINE = 'MyISAM';
Then you need to copy the result and execute it.
Demo
Update
If you need to execute it in one run, you can define a stored procedure with a cursor on information_schema.TABLES and execute it:
drop procedure if exists tmp_alter_myisam_to_innodb;
delimiter //
create procedure tmp_alter_myisam_to_innodb(in db_name text)
begin
declare done int default 0;
declare tbl text;
declare cur cursor for
select t.TABLE_NAME
from information_schema.TABLES t
where t.TABLE_SCHEMA = db_name
and t.ENGINE = 'MyISAM';
declare continue handler for not found set done = 1;
open cur;
fetch_loop: loop
fetch cur into tbl;
if done then
leave fetch_loop;
end if;
set #stmt = 'alter table `{table}` engine=innodb;';
set tbl = replace(tbl, '`', '``');
set #stmt = replace(#stmt, '{table}', tbl);
prepare stmt from #stmt;
execute stmt;
deallocate prepare stmt;
end loop;
close cur;
end //
delimiter ;
call tmp_alter_myisam_to_innodb('my_db');
drop procedure tmp_alter_myisam_to_innodb;

Related

Error when set auto increment in prodceduce

CREATE DEFINER = 'root'#'localhost'
PROCEDURE client_logging_system.Proc_client_Delete(IN in_clientID int)
COMMENT '
-- Parameter:
-- in_clientID: ID of client
'
BEGIN
DECLARE exit handler for sqlexception
BEGIN
ROLLBACK;
end;
START TRANSACTION;
DELETE FROM `client` WHERE `client`.ID = in_clientID;
ALTER TABLE `client` AUTO_INCREMENT = in_clientID;
COMMIT;
END
My proceduce get error on line:
ALTER TABLE `client` AUTO_INCREMENT = in_clientID;
Any suggestion for this problem?
You can't use variables in ALTER statements, it needs a literal number there. You'll need to create dynamic SQL using PREPARE.
SET #st = CONCAT('ALTER TABLE `client` AUTO_INCREMENT = ', in_clientID);
PREPARE stmt FROM #st;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
You can only use limited set of DDL statements inside a routine.
What are you trying to achive with the ALTER TABLE-statement? If you delete a client with id that is not the biggest one, the ALTER TABLE would not make sense.

How to change all tables engines from MYISAM to INNODB in multiple databses?

I know I can issue an alter table individually to change the table storage from MyISAM to InnoDB.
I am wondering if there is a way to quickly change all of them to InnoDB?
This is one-time task.
Use
SELECT CONCAT(TABLE_SCHEMA, '.', TABLE_NAME) tablename
FROM INFORMATION_SCHEMA.TABLES
WHERE ENGINE = 'MyISAM'
AND TABLE_SCHEMA IN (databases names list);
Get the tables list, build ALTERing script, and execute it manually.
This operation in stored procedure form. NOT TESTED!!!
CREATE PROCEDURE alter_engines ()
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE cur CURSOR FOR
SELECT CONCAT('ALTER TABLE ' , TABLE_SCHEMA, '.', TABLE_NAME, ' ENGINE = InnoDB;')
FROM INFORMATION_SCHEMA.TABLES
WHERE ENGINE = 'MyISAM'
/* do not forget */
AND TABLE_SCHEMA IN (databases names list);
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN cur;
alter_loop: LOOP
FETCH cur INTO #sql;
IF done THEN
LEAVE alter_loop;
END IF;
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END LOOP;
CLOSE cur;
END;

Mysql calling procedure failed when dynamically alter table in it

I want to alter my tables dynamically based on whether the table has specific column.
My database name is summer_cms, and there are over 50 tables in it.
What I want are below:
If a table has a column named add_time, then I would like to add a column add_user_id in it.
Similarly, I would like to add update_user_id in the table if update_time is found.
I know I should get it down in the process of creating the database schemas, but my database has been built and I have to alter it by need.
So I create a procedure to do it:
CREATE PROCEDURE ALTER_SUMMER_TABLE()
BEGIN
DECLARE tableName VARCHAR(64);
DECLARE exitence VARCHAR(64);
DECLARE ntable INT; # number of tables
DECLARE i INT; # index
SET i = 0;
# get the count of table
SELECT COUNT(DISTINCT(TABLE_NAME)) INTO ntable FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'summer_cms';
WHILE i < ntable DO
# select the specific table name into the variable of `tableName`.
SELECT TABLE_NAME INTO tableName
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = 'summer_cms'
AND COLUMN_NAME = 'add_time'
LIMIT 1 OFFSET i;
# alter table, but I get error in this clause.
ALTER TABLE tableName ADD COLUMN `add_user_id` INT NOT NULL DEFAULT 0 COMMENT 'add user id';
# check if the table has `update_time`
SELECT TABLE_NAME INTO exitence
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = 'summer_cms'
AND TABLE_NAME = tableName
AND COLUMN_NAME = 'update_time';
# add `update_user_id` if `update_time` be found.
IF exitence THEN
ALTER TABLE tableName ADD COLUMN `update_user_id` INT NOT NULL DEFAULT 0 COMMENT 'update user id';
END IF;
SET i = i + 1;
END WHILE;
END
But I got an error when I call this procedure.
Procedure execution failed
1146 - Table 'summer_cms.tableName' doesn't exist
Dose anyone could tell me what I was missing or wrong? Any help will be appreciated.
There a a few alterations you can make to your procedure to make it more streamlined as well as getting round a few problems.
First using a cursor to select the table names rather than using the two selects your using. Secondly to use a prepared statement to allow you to dynamically set the table name...
DELIMITER $$
CREATE DEFINER=`root`#`localhost` PROCEDURE `ALTER_SUMMER_TABLE`()
BEGIN
DECLARE done INT DEFAULT 0;
DECLARE tableName VARCHAR(64);
declare cur cursor for SELECT TABLE_NAME
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = 'summer_cms'
AND COLUMN_NAME = 'add_time';
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
open cur;
start_loop: loop
fetch cur into tableName;
if (done = 1 )THEN
LEAVE start_loop;
END IF;
SET #sql = CONCAT('ALTER TABLE ', tableName,' ADD COLUMN `add_user_id` INT NOT NULL DEFAULT 0 ');
PREPARE stmt FROM #sql;
EXECUTE stmt;
end loop;
close cur;
END$$
DELIMITER ;
You could do a few tweaks - only fetch table names where the column doesn't already exist for example.
Here's an example of dynamic sql
drop procedure if exists alter_table;
delimiter //
CREATE DEFINER=`root`#`localhost` procedure alter_table()
begin
declare tablename varchar(20);
set tablename = 'u';
set #sqlstmt = concat('ALTER TABLE ', tableName, ' ADD COLUMN ', char(96), 'add_user_id', char(96), ' INT NOT NULL DEFAULT 0 COMMENT', char(39), 'add user id', char(39),';');
prepare stmt from #sqlstmt;
execute stmt;
deallocate prepare stmt;
end //
delimiter ;
Note I have used ascii backticks and single quotes.

Drop a database if there is no table in it

Is there a MySQL command can do something like:
If there is nothing in a database, drop it. If there is any tables in it, do nothing.
Such as:
drop database if exists foobar
But:
drop database if empty foobar
Is there any way to do this?
As Barmar said you can use INFORMATION_SCHEMA.TABLES with stored procedure.
Here is my small effort:
DELIMITER //
CREATE PROCEDURE spDropDB_IF_Empty()
BEGIN
IF (SELECT COUNT(table_name) from INFORMATION_SCHEMA.tables
WHERE Table_Schema = 'mariadb')= 0 THEN
DROP DATABASE mariadb;
ELSE
SELECT 'There are tables in the mariaDB';
END IF;
END //
DELIMITER ;
Call SP:
CALL spDropDB_IF_Empty()
Hopefully this will help others as well. I just created a procedure for my own purpose after reading your question and commentators' comments.
use mysql;
-- switch to a different delimiter
delimiter $$
create procedure drop_empty_databases()
begin
declare table_schema varchar(200); -- will hold schema obtained from query
declare schema_end int default 0; -- temp variable that's set to 1 when reading of all schema is done
-- cursor that lists all schemas with no tables
declare cur cursor for
select s.schema_name
from information_schema.schemata s
left join information_schema.tables t on t.table_schema = s.schema_name
group by s.schema_name
having count(t.table_name) = 0;
-- set schema_end to 1 when we run out of schemas while looping
declare continue handler for not found set schema_end = 1;
open cur;
looper: loop
fetch cur into table_schema;
if schema_end = 1 then
leave looper;
end if;
set #sql = concat('drop database ', table_schema);
prepare stmt from #sql;
execute stmt;
end loop;
close cur;
end
$$
-- switch back to semi-colon delimiter
delimiter ;
Usage:
use mysql;
create database test123;
call drop_empty_databases(); -- test123 database should be gone after running this
Please test this on a non-production server and confirm that it does what you want it to do.

How to update several MySQL DBs together?

On the same server, I have around 30 sites, and the DB of 30 sites have the same structure, now I want to update a table: tx_tip(same table name on 30 DBs). instead of using phpmyadmin to update one by one, is there a way that I can use mysql command to update all the DBs together?
You can update multiple tables using a JOIN. If the tables are in different databases, you have to put the database prefix in the query.
UPDATE db1.tx_tip t1
JOIN db2.tx_tip t2
JOIN db3.tx_tip t3
...
SET t1.col = new_val,
t2.col = new_val,
t3.col = new_val,
WHERE <condition>
If you maintain this table on a regular basis you may consider to create a simple stored procedure that might look something like this
DELIMITER $$
CREATE PROCEDURE sp_update_tx_tip(IN p_id INT, IN p_new_value VARCHAR(128))
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE db_name VARCHAR(64);
DECLARE cur CURSOR FOR
SELECT TABLE_SCHEMA
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME = 'tx_tip';
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN cur;
SET #id = p_id, #new_value = p_new_value;
read_loop: LOOP
FETCH cur INTO db_name;
IF done THEN LEAVE read_loop; END IF;
SET #sql = CONCAT('UPDATE ', db_name, '.tx_tip t SET t.value = ? WHERE t.id = ?');
PREPARE stmt FROM #sql;
EXECUTE stmt USING #new_value, #id;
DEALLOCATE PREPARE stmt;
END LOOP;
CLOSE cur;
END$$
DELIMITER ;
And use it like
CALL sp_update_tx_tip(1, 'new value');
It will pull names of all databases that have tx_tip table from INFORMATION_SCHEMA.TABLES, construct and execute update statements for you.