Scheduling A Weekly Script for rebuild Mysql Index - mysql

We are planning to deploy a Stored Procedure to rebuild index. As per below specifictions on Mysql forum. Someone can send any sample script do the same for all table in a particular Database. Will there is any concern or any issues we could face for below script running on weekend.
ALTER TABLE t1 ENGINE = InnoDB;
https://dev.mysql.com/doc/refman/5.7/en/rebuilding-tables.html#rebuilding-tables-alter-table

Index's are kept up to date in InnoDB, statistics can get out of date. Post 5.6 even that is rarely needed to my knowledge. Running the Alter command will repair the table, which will repair any fragmentation. You can read about the different types of algorithms that are available to ALTER TABLE here:
ALTER TABLE
If however you want to check the database over, you can use the check mysql command which does ALTER as well as a few other bits n pieces to optimise index's, update statistics etc.
mysqlcheck

Below is a the mysql script to Optimize the Mysql DB. However as said earlier its rarely required since InnoDB is itself able to do the same.
USE VIPLamar;
DROP PROCEDURE IF EXISTS Lamar_Index_Rebuild_Script;
DELIMITER //
-- call Lamar_Index_Rebuild_Script();
CREATE PROCEDURE Lamar_Index_Rebuild_Script()
mysp:BEGIN
Declare v_max_counter int;
Declare v_counter int;
Declare v_query longtext;
Declare v_table_name varchar(1000);
Declare v_datetime datetime default now();
DROP temporary TABLE if exists temp_todo;
create temporary table temp_todo
(
id int auto_increment primary key,
`table_name` varchar(1000)
);
create table if not exists VIP_Lamar_Index_Rebuild_Script
(
id int auto_increment primary key,
`table_name` varchar(1000),
start_date datetime,
finish_date datetime
);
insert into temp_todo (table_name)
SELECT table_name FROM INFORMATION_SCHEMA.TABLES WHERE table_schema = DATABASE();
-- loop thru table
-- check if table exists
Select max(id) Into v_max_counter from temp_todo;
Set v_counter = 1;
WHILE v_counter <= v_max_counter
Do
select `table_name` into v_table_name from temp_todo where id = v_counter;
IF (EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.TABLES WHERE table_name = v_table_name AND table_schema = DATABASE()))
THEN
set v_query = concat('OPTIMIZE TABLE ' , v_table_name , ';');
set #stmt = v_query;
PREPARE tempstatement FROM #stmt;
EXECUTE tempstatement;
DEALLOCATE PREPARE tempstatement;
END if;
set v_counter = v_counter +1 ;
end while;
insert into VIP_Lamar_Index_Rebuild_Script(`table_name`,start_date,finish_date)
select table_name,v_datetime,now() from temp_todo
;
-- select * from VIP_Lamar_Index_Rebuild_Script;
-- drop table VIP_Lamar_Index_Rebuild_Script;
DROP temporary TABLE if exists temp_todo;
END;
//
DELIMITER ;

Related

How to add Auto-Increment to all database Tables with Primary key set

I use
ALTER TABLE `tablename` DROP PRIMARY KEY;
ALTER TABLE `tablename` MODIFY `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY;
To add Auto increment to a specific table with column name ID, However, what I want is to:
1 - Add Auto-Increment to all tables in a particular database (even if there are 1000 tables) with column name ID.
2 - Check if primary key is present in each table name with column name ID to be able to alter it.
The reason is I have a database with over 2,000 tables and after upgrading my xammp version, it seems to remove the auto-increment but retains primary key value.
We can use a cursor to loop through all tables which has an ID column in a specific database. Then use prepared statement to execute necessary alter table statements. Everything is done in a procedure . It's written and tested in workbench. Please note, based on your illustration, this is assuming all your tables' PK in the database are on column 'ID',which means NO OTHER columns are PK. Additionally, NO OTHER columns are auto_increment.
create database testdatabase;
use testdatabase;
drop table if exists t1;
drop table if exists t2;
drop table if exists t3;
create table t1 (id int,num int);
create table t2 (id int primary key, num int);
create table t3 (id int primary key auto_increment, num int);
delimiter //
drop procedure if exists add_pk_ai//
create procedure add_pk_ai(db_name varchar(20),col_name varchar(20),dt_type varchar(20)) -- in parameters include the database name, column name ,and the data type of the column
begin
declare tb_name varchar(20);
declare col_key varchar(20);
declare col_extra varchar(20);
declare done bool default false;
declare c cursor for select table_name,column_key,extra
from information_schema.columns
where table_schema=db_name and column_name=col_name and data_type=dt_type;
declare continue handler for not found set done=true;
open c;
lp:loop
fetch c into tb_name,col_key,col_extra;
if done=true then
leave lp;
end if;
if col_key!='PRI' then
set #stmt=concat('alter table ',db_name,'.',tb_name,' add primary key (',col_name,');');
prepare stmt1 from #stmt;
execute stmt1;
deallocate prepare stmt1;
end if;
if col_extra!='auto_increment' then
set #stmt=concat('alter table ',db_name,'.',tb_name,' modify ',col_name,' ',dt_type,' auto_increment ;');
prepare stmt2 from #stmt;
execute stmt2;
deallocate prepare stmt2;
end if;
end loop lp;
end//
delimiter ;
-- let's test the procedure
call add_pk_ai('testdatabase','id','int');
desc t1;
-- result set:
# Field, Type, Null, Key, Default, Extra
id, int(11), NO, PRI, , auto_increment
num, int(11), YES, , ,
desc t2;
-- result set:
# Field, Type, Null, Key, Default, Extra
id, int(11), NO, PRI, , auto_increment
num, int(11), YES, , ,
desc t3;
-- result set:
# Field, Type, Null, Key, Default, Extra
id, int(11), NO, PRI, , auto_increment
num, int(11), YES, , ,

Prepared Statement get wrong result in MYSQL

I have a table with design
CREATE TABLE IF NOT EXISTS InsuranceContract (
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
`enquiryCode` VARCHAR(20) DEFAULT NULL,
`contractCode` VARCHAR(20) DEFAULT NULL,
`createdAt` DATETIME DEFAULT CURRENT_TIMESTAMP (),
`updatedAt` DATETIME DEFAULT CURRENT_TIMESTAMP () ON UPDATE CURRENT_TIMESTAMP (),
UNIQUE KEY (`enquiryCode`)) ENGINE=INNODB DEFAULT CHARSET=UTF8 COLLATE = UTF8_BIN;
Then I was created a procedure like this
DROP procedure IF EXISTS `sp_insurance_contract_get`;
DELIMITER $$
CREATE PROCEDURE `sp_insurance_contract_get` (enquiryCode VARCHAR(20), contractCode VARCHAR(20))
BEGIN
SET #t1 = "SELECT * FROM InsuranceContract
WHERE InsuranceContract.enquiryCode = enquiryCode
AND InsuranceContract.contractCode = contractCode;";
PREPARE param_stmt FROM #t1;
EXECUTE param_stmt;
DEALLOCATE PREPARE param_stmt;
END$$
DELIMITER ;
And I was executed this procedure in MySQL Workbench by this command:
CALL sp_insurance_contract_get('EQ000000000014', '3001002');
I expected I will receive 1 row result but it selected all records in this table.
If I copy and create exactly this #t1 into plain SQL not using statement, it's correct.
Please help me to fix this error. I'm using MySQL 8.0.19
You can use placehoders on prepare statements, this is why we use them to prevent sql injection
One other thing never use column names as variables names, databases can not differentiate
DROP procedure IF EXISTS `sp_insurance_contract_get`;
DELIMITER $$
CREATE PROCEDURE `sp_insurance_contract_get` (enquiryCode_ VARCHAR(20), contractCode_ VARCHAR(20))
BEGIN
SET #t1 = "SELECT * FROM InsuranceContract
WHERE enquiryCode = ?
AND contractCode = ?;";
PREPARE param_stmt FROM #t1;
SET #a = enquiryCode_;
SET #b = contractCode_;
EXECUTE param_stmt USING #a, #b;
DEALLOCATE PREPARE param_stmt;
END$$
DELIMITER ;
When you say
WHERE enquiryCode = enquiryCode
you compare that named column to itself. The result is true always (unless the column value is NULL).
Change the names of your SP's parameters, so you can say something like
WHERE enquiryCode_param = enquiryCode
and things should work.
Notice that you have no need of a MySql "prepared statement" here. In the MySql / MariaDb world prepared statements are used for dynamic SQL. That's for constructing statements within the server from text strings. You don't need to do that here.

MySQL - how to alter column on all databases with single action?

I have a shared server where i have 50 to 60 databases each database has 200 tables.
How to add few new column's on all the existing databases in one go instead of one database after another manually?
ALTER TABLE `users` ADD `a` VARCHAR( 200 ) NULL;
ALTER TABLE `users` ADD `b` VARCHAR( 200 ) NULL;
ALTER TABLE `users` ADD `c` VARCHAR( 200 ) NULL;
ALTER TABLE `users` ADD `d` VARCHAR( 200 ) NULL;
ALTER TABLE `users` ADD `e` VARCHAR( 200 ) NULL;
ALTER TABLE `users` ADD `f` VARCHAR( 200 ) NULL;
You can create procedure for the same. And in procedure you can wrote cursor on this query.
select TABLE_NAME from INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA='sampledata';
Make dynamic ALTER statement using Dynamic Query:
DELIMITER $$
CREATE PROCEDURE `SAMPLEPROCEDURE`(IN COLUMNNAME VARCHAR(40))
BEGIN
DECLARE VAR_TABLENAME VARCHAR(100);
DECLARE DONE INT;
DECLARE CUR CURSOR FOR
SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'SAMPLEDATA';
DECLARE CONTINUE HANDLER FOR NOT FOUND SET DONE=1;
SET DONE = 0;
OPEN CUR;
TABLELOOP: LOOP
FETCH CUR INTO VAR_TABLENAME;
IF DONE = 1 THEN LEAVE TABLELOOP; END IF;
SET #VAR_ALTER_QUERY =CONCAT("ALTER TABLE ",VAR_TABLENAME," ADD ",COLUMNNAME," VARCHAR(200) NULL");
PREPARE STMT FROM #VAR_ALTER_QUERY;
EXECUTE STMT;
DEALLOCATE PREPARE STMT;
END LOOP TABLELOOP;
END
You can took above procedure as reference for same.
use information_schema
select
case when table_schema is not null then
CONCAT("USE ",TABLE_SCHEMA) end use_schema ,
CONCAT("Alter Table '", TABLE_SCHEMA,"'.'", TABLE_NAME, " Add 'a' varchar(200)") as MySQLCMD
from TABLES
where table_name = 'USERS';
The point is that you could use the dictionary schema to retrieve each 'user' tables from each schema and generate the ALTER scripts.

Mysql Cursor not sorting result in correct order

This is an abstraction of my original code as it´ll be easier to read for you guys.
I´m new to Mysql Storage procedures and from Cursors.
Whats happening is that Cursor is not bringing the results properly sorted as I set the ORDER BY instruction on the query.
Here´s all the structure and data for the tables to reproduce the issue.
Log Table :
DROP TABLE IF EXISTS `log`;
CREATE TABLE `log` (
`key` text NOT NULL,
`value` text NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Test Table :
DROP TABLE IF EXISTS `test1`;
CREATE TABLE `test1` (
`ID` bigint(8) unsigned NOT NULL AUTO_INCREMENT,
`price` float(16,8) NOT NULL,
PRIMARY KEY (`ID`),
KEY `price` (`price`)
) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=latin1;
Test table data :
INSERT INTO `test1` (`price`)
VALUES (100),(200),(300),(400),(300),(200),(100);
Query:
SELECT *
FROM `test1`
ORDER BY price DESC;
Expected results works fine with query directly:
4 - 400.00000000
5 - 300.00000000
3 - 300.00000000
6 - 200.00000000
2 - 200.00000000
7 - 100.00000000
1 - 100.00000000
Stored Procedure
DROP PROCEDURE IF EXISTS `test_proc1`;
DELIMITER ;;
CREATE DEFINER=`root`#`localhost` PROCEDURE `test_proc1`()
BEGIN
DECLARE done INT DEFAULT 0;
DECLARE ID BIGINT(8);
DECLARE price FLOAT(16,8);
DECLARE cur1 CURSOR FOR
SELECT * FROM `test1` ORDER BY price DESC; #Exact Query
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
START TRANSACTION;
OPEN cur1;
#Cleaning log
TRUNCATE `log`;
read_loop:
LOOP
FETCH cur1 INTO ID,price;
IF done = 1 THEN
LEAVE read_loop;
END IF;
#Inserting data to log
INSERT INTO `log`
VALUES (ID,price);
END LOOP read_loop;
CLOSE cur1;
COMMIT;
#Bring log for result
SELECT * FROM log;
END;;
DELIMITER ;
Call procedure
CALL test_proc1();
The CURSOR has exactly the same query as I posted at the top, you can check that on the Stored Procedure. But when I loop through it, I get another order.
15 100.00000000
21 100.00000000
16 200.00000000
20 200.00000000
17 300.00000000
19 300.00000000
18 400.00000000
Whats going on? Can somebody help me on this?
I also tried nesting the query like this with no fix at all.
SELECT * FROM(
SELECT *
FROM `test1`
ORDER BY price DESC) AS tmp_tbl
Looks like you have a "variable collision". Variable price is used instead of table column with that exact name. Change variable name, or use table alias like this:
SELECT * FROM `test1` as `t` ORDER BY `t`.`price` DESC;

MySQL variables in ALTER TABLE script

Hello The following procedure will have to move all constraints from one table to the other however I am having some difficulties at the point where the constraint should be deleted.
The problem: how do I use variables in the following line
ALTER TABLE var_referenced_table_name DROP FOREIGN KEY var_constraint_name;
when I use as is, I receive the following error
Error Code: 1146. Table 'oaf_businesslink_dev.var_referenced_table_name' doesn't exist
MySQL does not recognise var_referenced_table_name and var_constraint_name as variables.
DELIMITER //
DROP PROCEDURE IF EXISTS AlterConstraints//
CREATE PROCEDURE AlterConstraints()
BEGIN
DECLARE schema_name VARCHAR(60) DEFAULT 'oaf_businesslink_dev';
DECLARE table_name VARCHAR(60) DEFAULT 'wp_systemuser';
DECLARE finished INTEGER DEFAULT 0;
DECLARE total INTEGER DEFAULT 0;
DECLARE var_constraint_name VARCHAR(60) DEFAULT '';
DECLARE var_table_name VARCHAR(60) DEFAULT '';
DECLARE var_column_name VARCHAR(60) DEFAULT '';
DECLARE var_referenced_table_name VARCHAR(60) DEFAULT '';
DECLARE var_referenced_column_name VARCHAR(60) DEFAULT '';
DECLARE cur_constraints CURSOR FOR SELECT constraint_Name, table_name,column_name,referenced_table_name,referenced_column_name
FROM information_schema.key_column_usage
WHERE constraint_schema = schema_name
AND referenced_table_name = table_name
AND table_name IS NOT NULL;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET finished = 1;
OPEN cur_constraints;
get_constraint:
LOOP FETCH cur_constraints
INTO var_constraint_name
,var_table_name
,var_column_name
,var_referenced_table_name
,var_referenced_column_name;
IF finished THEN
LEAVE get_constraint;
END IF;
/* Get Constraint Count */
SET total = total + 1;
/* Remove Constraint */
IF EXISTS(SELECT * FROM information_schema.TABLE_CONSTRAINTS
WHERE CONSTRAINT_NAME = var_constraint_name AND TABLE_NAME = var_referenced_table_name AND TABLE_SCHEMA = schema_name)
THEN
/*
* Error Code: 1146. Table 'oaf_businesslink_dev.var_referenced_table_name' doesn't exist
*/
ALTER TABLE var_referenced_table_name DROP FOREIGN KEY var_constraint_name;
END IF;
/* Change Datatype to BIGINT */
/* Recreate Constraint to new table */
END
LOOP get_constraint;
CLOSE cur_constraints;
SELECT total;
END
//
DELIMITER ;
CALL AlterConstraints();
Thanks in advance.
With the use of variables as column names and tables, it would be best to DECLARE a query as a "string" and then execute that string via a Prepared Statement.
This can be done in two ways, either by CONCAT() to build the full string or by using PREPARE with arguments:
SET #query = CONCAT('ALTER TABLE ', var_referenced_table_name, ' DROP FOREIGN KEY ', var_constraint_name, ';');
PREPARE stmt FROM #query;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;