I have this piece of code that runs 'optimize table' to every table of a schema.
However, I need to run it to every table in every schema. How can I do that without causing problems?
set #a=null,#c=null,#b=concat("show tables where",ifnull(concat(" `Tables_in_",database(),"` like '",#c,"' and"),'')," (#a:=concat_ws(',',#a,`Tables_in_",database(),"`))");
Prepare `bd` from #b;
EXECUTE `bd`;
DEALLOCATE PREPARE `bd`;
set #a:=concat('optimize table ',#a);
PREPARE `sql` FROM #a;
EXECUTE `sql`;
DEALLOCATE PREPARE `sql`;
set #a=null,#b=null,#c=null;
Here is a stored procedure for your purpose.
It works by first querying INFORMATION_SCHEMA.TABLES to retrieve the list of schemas and table names. Then it goes into a loop and executes the OPTIMIZE TABLE commands sequentially.
Important note: running such a command on all tables is probably not a good idea. Specifically, you don't (or can't) adress MySQL built-in schemas. I added a WHERE clause in the query that excludes the following schemas: information_schema, performance_schema, mysql and sys. I would suggest that you further restrict the WHERE clause to your specific need (having a fixed list of schema would be a good idea).
DELIMITER $$
CREATE PROCEDURE OptimizeAllTables()
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE tableName CHAR(100);
DECLARE tableSchema CHAR(100);
DECLARE tableList CURSOR FOR
SELECT table_schema, table_name
FROM information_schema.tables
WHERE table_schema NOT IN ('information_schema', 'mysql', 'performance_schema', 'sys');
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN tableList;
tableListLoop: LOOP
SET done = FALSE;
FETCH tableList INTO tableSchema, tableName;
IF done THEN
LEAVE tableListLoop;
END IF;
SET #VarSQL = CONCAT('OPTIMIZE TABLE `', tableSchema, '`.`', tableName, '`');
-- SELECT #VarSQL;
PREPARE stmt FROM #VarSQL;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END LOOP;
CLOSE tableList;
END$$
I would also strongly suggest that you run this procedure in debug mode before executing it for real. For this, you can change this block:
-- SELECT #VarSQL;
PREPARE stmt FROM #VarSQL;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
To:
SELECT #VarSQL;
-- PREPARE stmt FROM #VarSQL;
-- EXECUTE stmt;
-- DEALLOCATE PREPARE stmt;
This will show the commands without actually executing them.
Demo on DB Fiddle (in debug mode):
-- create a few tables for testing
create table table1(id int primary key);
create table table2(id int primary key);
create table table3(id int primary key);
-- call the procedure
call OptimizeAllTables();
Yields:
| #VarSQL |
| ------------------------------ |
| OPTIMIZE TABLE `test`.`table1` |
| #VarSQL |
| ------------------------------ |
| OPTIMIZE TABLE `test`.`table2` |
| #VarSQL |
| ------------------------------ |
| OPTIMIZE TABLE `test`.`table3` |
Related
I am facing a problem that I can't solve or explain.
My goal:
Create a stored procedure for MySQL to execute action like explode/split function in order to use the result of this procedure as a table.
My Procedure :
DROP PROCEDURE IF EXISTS `P_string_split`;
DELIMITER |
CREATE PROCEDURE
P_string_split(IN p_delimiter TEXT, IN p_text TEXT, IN p_limit INT, IN out_table_name TEXT)
BEGIN
DECLARE var_str_text TEXT;
DECLARE var_str_index INT;
DECLARE var_delimiter TEXT;
SET var_delimiter = p_delimiter;
SET var_str_index = 1;
DROP TABLE IF EXISTS TT_P_string_split;
CREATE TEMPORARY TABLE TT_P_string_split
(
str_index INT PRIMARY KEY,
str_text TEXT
);
while(LENGTH(p_text) > 0 AND (p_limit = 0 OR (p_limit > 0 AND p_limit > var_str_index)))
DO
SET var_str_text = SUBSTRING_INDEX(p_text, var_delimiter, 1);
INSERT INTO TT_P_string_split (str_index, str_text) VALUES (var_str_index, var_str_text);
SET var_str_index = var_str_index + 1;
SET p_text = SUBSTRING(p_text, LENGTH(var_str_text) + LENGTH(var_delimiter) + 1);
END WHILE;
IF LENGTH(p_text) > 0 THEN
INSERT INTO TT_P_string_split (str_index, str_text) VALUES (var_str_index, p_text);
END IF;
SET #QUERY_TMP = CONCAT('
DROP TABLE IF EXISTS `',out_table_name,'`;
CREATE TEMPORARY TABLE `',out_table_name,'` (
`str_index` INT PRIMARY KEY,
`str_text` TEXT );
INSERT INTO ',out_table_name,' (str_index,str_text)
SELECT str_index,str_text
FROM TT_P_string_split;');
# Area with problems
# ==================================
SELECT #QUERY_TMP;
PREPARE stmt FROM #QUERY_TMP;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
# ==================================
END |
DELIMITER ;
Execution command :
CALL P_string_split(',', 'plop,plip,plup,plap', 0, 'PLOUP');
Problem is located in : "Area with problems" named after FAREA
If the code in the FAREA is as follows (Variation 1) :
SELECT #QUERY_TMP;
# PREPARE stmt FROM #QUERY_TMP;
# EXECUTE stmt;
# DEALLOCATE PREPARE stmt;
The output is :
#QUERY_TMP
===============================================================
DROP TABLE IF EXISTS `PLOUP`;
CREATE TEMPORARY TABLE `PLOUP` (
`str_index` INT PRIMARY KEY,
`str_text` TEXT );
INSERT INTO PLOUP (str_index,str_text)
SELECT str_index,str_text
FROM TT_P_string_split;
But if the code in the FAREA is complete (Variation 2) :
SELECT #QUERY_TMP;
PREPARE stmt FROM #QUERY_TMP;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
The output is :
#QUERY_TMP
===============================================================
<No row>
And and error is throw :
[42000][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 'CREATE TEMPORARY TABLE `PLOUP` (
`str_index` INT PRIMARY KEY,
' at line 2
I have the impression that the addition of the statement executing part truncates the value of my variable.
Am I missing something?
Thanks in advance.
Edit 1 :
This query :
SELECT *
FROM performance_schema.user_variables_by_thread
WHERE VARIABLE_NAME = 'QUERY_TMP';
Return :
2440,QUERY_TMP,"
DROP TABLE IF EXISTS `PLOUP`;
CREATE TEMPORARY TABLE `PLOUP` (
`str_index` INT PRIMARY KEY,
`str_text` TEXT );
INSERT INTO PLOUP (str_index,str_text)
SELECT str_index,str_text
FROM TT_P_string_split;"
I have fixed the comportment by separate execution.
SET #QUERY_TMP = CONCAT('DROP TABLE IF EXISTS `',out_table_name,'`;');
PREPARE stmt FROM #QUERY_TMP;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET #QUERY_TMP = CONCAT('CREATE TEMPORARY TABLE `',out_table_name,'` (
`str_index` INT PRIMARY KEY, `str_text` TEXT ) ENGINE = InnoDB;');
PREPARE stmt FROM #QUERY_TMP;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET #QUERY_TMP = CONCAT('INSERT INTO ',out_table_name,' (str_index,str_text)
SELECT str_index,str_text
FROM TT_P_string_split;');
PREPARE stmt FROM #QUERY_TMP;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
This is the sql i'm using based from this answer:
SET #pattern = '%_movielist';
SELECT concat('TRUNCATE TABLE ', GROUP_CONCAT(concat(TABLE_NAME)), ';')
INTO #truncatelike FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME LIKE #pattern;
SELECT #truncatelike;
PREPARE stmt FROM #truncatelike;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
but I get this error Access denied for user 'root'#'%' to database 'information_schema'.
What am I doing wrong? It seems to work for other users
You trying to execute this statement on "information_schema" database. Read more about this database [https://dev.mysql.com/doc/refman/5.7/en/information-schema.html]
You should not be running statements on the information_schema database (unless you REALLY know what you're doing). The database serves as a "meta" repository that dictates how the server operates. Chances are that you have no need to touch it and you'll likely brick your server if you do.
This is already answered here. [#1044 - Access denied for user 'root'#'localhost' to database 'information_schema'
Restriction to above: This query will work only if the no of table returned by the statement is 1 for more than 1 tables, you will require to use it in iteration.
To make this work for all the table matching the pattern we would require to use stored procedure.
Please change the Procedure name
CREATE PROCEDURE `new_procedure`()
BEGIN
-- Pattern to Match
SET #pattern = '%_movielist';
-- Temporary Table to Store the Result of The Select Statement
CREATE TEMPORARY TABLE IF NOT EXISTS Table_ToBeTruncated
(
Id int NOT NULL AUTO_INCREMENT,TableName varchar(100),
PRIMARY KEY (id)
);
-- Insert all the TableName to be Truncated
insert Table_ToBeTruncated(TableName)
SELECT distinct concat('TRUNCATE TABLE `', TABLE_NAME, '`;')
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME LIKE #pattern and TABLE_SCHEMA = 'movielist';
-- Declare a variable to count the no of records to be truncated.
SET #count=(Select count(*)from Table_ToBeTruncated);
-- Iterate the list
WHILE #count> 0 DO
-- Pick One table from the Temporary Table List;
SELECT TableName into #truncatelike from Table_ToBeTruncated where ID= #count;
-- Prepare the statement
PREPARE stmt FROM #truncatelike;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- Decrease the counter.
set #count = #count- 1;
END WHILE;
drop TEMPORARY TABLE IF EXISTS Table_ToBeTruncated ;
END
I'm looking for a way to generate valid HTML code within MySQL (without PHP) by converting any query output into an HTML table.
Here's my progress so far and evidently, I'm stuck. I hope I can get some help, thanks.
1. "dynSQL" - A procedure to take any Select query and create a named table out of it
Since MySQL doesn't allow dynamic queries in functions, I'm calling a procedure that creates a named table, tmp. I can't use a temporary table because info about temporary tables is not available in information_schema (in mysql 5.6)
CREATE DEFINER=`root`#`%` PROCEDURE `dynSQL`(SQL_QUERY TEXT)
BEGIN
set #SQLQ := 'Drop table if exists tmp;';
PREPARE stmt from #SQLQ;
Execute stmt;
SET #SQLQ := concat('create table tmp as ',SQL_QUERY);
PREPARE stmt from #SQLQ;
Execute stmt;
-- I'm adding a auto increment ID column to be able to loop through the rows later
SET #SQLQ := "ALTER TABLE tmp add column CustColHTML_ID INT NOT NULL AUTO_INCREMENT FIRST, ADD primary KEY Id(CustColHTML_ID)";
PREPARE stmt from #SQLQ;
Execute stmt;
DEALLOCATE PREPARE stmt;
END
2. "MakeHTML" - Function to read from the table tmp and return a formatted HTML table
CREATE DEFINER=`root`#`%` FUNCTION `MakeHTML`() RETURNS text CHARSET utf8
DETERMINISTIC
BEGIN
DECLARE HTML text default "<TABLE><TR>";
DECLARE rowCount int default 0;
DECLARE i int default 0;
select concat('<TR>',group_concat('<TD>',column_name,'</TD>' separator ''),'</TR>') into html from information_Schema.`columns` where table_name='tmp';
Select max(CustColHTML_ID) into rowCount from `tmp`; -- Set the row counter
WHILE i<=rowCount DO
-- What do I do here? How do I loop through the columns of table tmp?
set i:=i+1;
END WHILE;
RETURN HTML;
END
As you can see, I'm stuck at looping through the unknown and dynamic columns of table tmp. I read about how a cursor can be used here, but all the examples I saw make use of known columns and assign those into named variables. However, since the query itself is dynamic, I wouldn't know the names of the columns.
I'd really appreciate your time and assistance, thanks!
p.s. I've posted this as a new question because my earlier question was marked as closed as being too broad. I subsequently edited my question but it was still showing as Closed. I've therefore deleted the older question and replaced it with this one.
With a sample table as such:
CREATE TABLE tmp (ID INT, Col1 INT, Col2 INT);
The SQL you would need to generate your HTML is:
SELECT CONCAT('<table>', GROUP_CONCAT(CONCAT('<tr><td>',ID,'</td><td>',Col1,'</td><td>',Col2,'</td><tr>')), '</table>')
FROM tmp;
You can generate this using the INFORMATION_SCHEMA:
SELECT CONCAT
(
'SELECT CONCAT(''<table>'', GROUP_CONCAT(CONCAT(''<tr>'', ',
GROUP_CONCAT(CONCAT('''<td>'',', COLUMN_NAME, ',''</td>''')),
', ''</tr>'')), ''</table>'') FROM tmp'
)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'tmp';
It is then just a case of executing this:
SET #SQL = (
SELECT CONCAT
(
'SELECT CONCAT(''<table>'', GROUP_CONCAT(CONCAT(''<tr>'', ',
GROUP_CONCAT(CONCAT('''<td>'',', COLUMN_NAME, ',''</td>''')),
', ''</tr>'')), ''</table>'') FROM tmp'
)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'tmp'
);
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
Example on SQL Fiddle
ADDENDEUM
Forgot to include table headers:
SET #SQL = (
SELECT CONCAT
(
'SELECT CONCAT(''<table><tr>'',',
GROUP_CONCAT(CONCAT('''<th>'',''', COLUMN_NAME, ''',''</th>''')),
', ''</tr>'', GROUP_CONCAT(CONCAT(''<tr>'', ',
GROUP_CONCAT(CONCAT('''<td>'',', COLUMN_NAME, ',''</td>''')),
', ''</tr>'')), ''</table>'') FROM tmp'
)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'tmp'
);
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
Example on SQL Fiddle
I want to create a sql file for my dba that will check whether index exists on a table. IF doesn't exist-create it.
I found many examples that use stored procedure, but I only want to run it once.
Something like this:
-- Creates an index if it does not already exist in MySQL.
START TRANSACTION;
SET IndexIsThere = 0;
SET given_table = 'IDR_CHGS';
SET given_index = 'FK_IDR_PATIENT_PT_ID_idx1';
SELECT COUNT(1) INTO IndexIsThere
FROM INFORMATION_SCHEMA.STATISTICS
WHERE table_name = given_table
AND index_name = given_index;
IF IndexIsThere = 0 THEN
SET #sqlstmt = CONCAT('CREATE INDEX ',given_index,' ON ', given_database,'.',given_table,' (',given_columns,')');
PREPARE st FROM #sqlstmt;
EXECUTE st;
DEALLOCATE PREPARE st;
SELECT CONCAT('Created index ', given_table,'.', given_index, ' on columns ', given_columns) AS 'CreateIndex status';
ELSE
SELECT CONCAT('Index ', given_index,' Already Exists on Table ', given_database,'.',given_table) AS 'CreateIndex status';
END IF;
COMMIT;
Is this doable?
MySQL doesn't support IF/THEN/ELSE constructs outside of stored routines or triggers.
If you use PREPARE and EXECUTE, you could simply form a string that has the CREATE INDEX statement if there is no index, and a no-op statement (e.g. a comment) if not.
SELECT COALESCE(CONCAT('SELECT \'index ', S.INDEX_NAME, ' exists already\''),
'CREATE INDEX `idx_x` ON test.foo (`x`)') INTO #sql
FROM (SELECT NULL) AS d
LEFT OUTER JOIN INFORMATION_SCHEMA.STATISTICS AS S
ON (S.TABLE_SCHEMA, S.TABLE_NAME, S.INDEX_NAME) = ('test', 'foo', 'idx_x');
PREPARE s from #sql;
EXECUTE s;
If the index exists, #sql will be:
mysql> select #sql;
+-------------------------------------+
| #sql |
+-------------------------------------+
| SELECT 'index idx_x exists already' |
+-------------------------------------+
If the index does not exist, #sql will be:
mysql> select #sql;
+----------------------------------------+
| #sql |
+----------------------------------------+
| CREATE INDEX `idx_x` ON test.foo (`x`) |
+----------------------------------------+
By the way, all CREATE and ALTER statements implicitly commit before and after the statement, so there's no purpose in using start transaction and commit in the way you're doing. See http://dev.mysql.com/doc/refman/5.6/en/implicit-commit.html
I'm trying to create a table with a name based on the current year and month(2011-09), but MySQL doesn't seem to like this.
SET #yyyy_mm=Year(NOW())+'-'+Month(NOW());
CREATE TABLE `survey`.`#yyyy_mm` LIKE `survey`.`interim`;
SHOW TABLES IN `survey`;
+-----------+
| interim |
+-----------+
| #yyyy_mm |
+-----------+
If I do CREATE TABLE; without the ticks around #yyyy_mm, I get a generic syntax error.
#yyyy_mm resolves to 2020.
You should be able to do something like this:
SET #yyyy_mm=DATE_FORMAT(now(),'%Y-%m');
SET #c = CONCAT('CREATE TABLE `survey`.`',#yyyy_mm, '` LIKE `survey`.`interim`');
PREPARE stmt from #c;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
set #yyyy_mm=concat(year(now()),'-',month(now()));
set #str = concat('create table survery.`', #yyyy_mm,'` like survey.interim;');
prepare stmt from #str;
execute stmt;
deallocate prepare stmt;