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.
Related
I have a T-SQL query which create database if it does not exist yet:
IF (NOT EXISTS (SELECT name
FROM master.dbo.sysdatabases
WHERE ('[' + 'DBName' + ']' = 'DBName'
OR name = 'DBName')))
BEGIN
CREATE DATABASE DBName
PRINT 'DATABASE_CREATED'
END
ELSE
PRINT 'DATABASE_EXIST'
When I want use this in MySQL I get an error:
'IF' is not valid input at this postion
I change this script as
IF(SELECT COUNT(*) FROM SCHEMA_NAME
FROM INFORMATION_SCHEMA.SCHEMATA
WHERE SCHEMA_NAME = 'DBName') > 0)
THEN BEGIN
CREATE DATABASE DBName
PRINT 'DATABASE_CREATED'
ELSE
PRINT 'DATABASE_EXIST'`
but it still doesn't work
How can I create this query in MySQL?
I'm not sure exactly how you'd check, but if you just want to create it if it doesn't exist, then you can do
CREATE DATABASE IF NOT EXISTS DBname
Here is the example in a helper (permanent) database. That db's name is permanent
One time db create:
create schema permanent;
Now make sure you
USE permanent;
then
Stored Proc:
DROP PROCEDURE IF EXISTS createDB;
DELIMITER $$
CREATE PROCEDURE createDB(IN pDbName VARCHAR(100))
BEGIN
DECLARE preExisted INT;
DECLARE ret VARCHAR(50);
SET ret='DATABASE_EXIST';
SELECT COUNT(*) INTO preExisted
FROM INFORMATION_SCHEMA.SCHEMATA
WHERE SCHEMA_NAME=pDbName;
IF preExisted=0 THEN
SET #sql=CONCAT('CREATE SCHEMA ',pDbName); -- add on any other parts of string like charset etc
PREPARE stmt1 FROM #sql;
EXECUTE stmt1;
DEALLOCATE PREPARE stmt1;
-- right here you could assume it worked or take additional
-- step to confirm it
SET ret='DATABASE_CREATED';
END IF;
SELECT ret as 'col1';
END$$
DELIMITER ;
Test:
use permanent;
call createDB('xyz');
-- returns col1 DATABASE_CREATED
call createDB('xyz');
-- returns col1 DATABASE_EXIST
I've been trying to dynamically drop tables, procedures, and functions in MySQL. I'm doing this because I am dynamically creating them for a project, when the project version changes I need to clean up and rebuild it.
I can dynamically drop tables, however; I cannot dynamically drop procedures and functions.
Here is an example of the code I am using:
DELIMITER ;;
DROP PROCEDURE IF EXISTS md_remove_project; ;;
CREATE PROCEDURE md_remove_project()
begin
DECLARE TableName text;
DECLARE ProcName text;
DECLARE done int DEFAULT false;
DECLARE statement text;
DECLARE table_cursor CURSOR FOR
SELECT table_name FROM tmp_md_tables;
DECLARE proc_cursor CURSOR FOR
SELECT routine_name FROM tmp_md_procedures;
DECLARE func_cursor CURSOR FOR
SELECT routine_name FROM tmp_md_functions;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = true;
# Drop all the 'md' tables..............................................
# This Works...
DROP TABLE IF EXISTS tmp_md_tables;
CREATE TEMPORARY TABLE tmp_md_tables
SELECT
table_name
FROM
information_schema.tables
WHERE
table_name LIKE 'md_%';
OPEN table_cursor;
table_loop: LOOP
FETCH table_cursor INTO TableName;
IF done THEN
LEAVE table_loop;
END IF;
SET #statement = CONCAT('DROP TABLE IF EXISTS ', TableName, ';');
PREPARE STATEMENT FROM #statement;
EXECUTE STATEMENT;
DEALLOCATE PREPARE STATEMENT;
END LOOP;
CLOSE table_cursor;
DROP TABLE IF EXISTS tmp_md_tables;
#-----------------------------------------------------------------------
# Drop all the 'md' procedures............................................
DROP TABLE IF EXISTS tmp_md_procedures;
CREATE TEMPORARY TABLE tmp_md_procedures
SELECT
routine_name
FROM
information_schema.routines
WHERE
routine_type = 'PROCEDURE'
and
routine_name LIKE 'md_%';
SET done = false;
OPEN proc_cursor;
proc_loop: LOOP
FETCH proc_cursor INTO ProcName;
IF ProcName = 'md_remove_project' THEN
ITERATE proc_loop;
END IF;
IF done THEN
leave proc_loop;
END IF;
SET #statement = CONCAT('DROP PROCEDURE IF EXISTS ', ProcName, ';');
PREPARE STATEMENT FROM #statement;
EXECUTE STATEMENT;
DEALLOCATE PREPARE STATEMENT;
END LOOP;
CLOSE proc_cursor;
DROP TABLE IF EXISTS tmp_md_procedures;
END;
;;
DELIMITER ;
#CALL md_remove_project;
So I create a table with the procedures named md_%, then I loop through the table. For each routine_name, I prepare a statement to drop the procedure. Then I get the following message:
Error Code: 1295. This command is not supported in the prepared statement protocol yet
Are there any other solutions to drop procedures like 'md_%' ???
Thank You.
When using mysqli_... functions, there is no need to attempt to change the delimiter. The change delimiter command is only needed when using the MySQL (command line) Client. The command is, in fact, a MySQL client command (the client never sends it to the server).
The server is smart enough to recognize the CREATE PROCEDURE command and knowns it ends with END;
As a result, you can simply do two queries: first the DROP PROCEDURE IF EXISTS ... followed by the CREATE PROCEDURE ... END; query.
If you must do them in a single call, you could use mysqli::multi_query but I would recommend against it (because of possible serious security implications).
Assuming a database with a ridiculous amount of tables (200+), how can I perform SELECT * FROM <> LIMIT 3; where <> represents all the tables in the database? My goal is to get an idea of what each table contains, and the column names shown in DESCRIBE are not particularly useful. Therefore I would like to see 3 records from each table.
I know that I could easily script this in PHP by iterating over the output of show tables; however I am looking for a command to run on the MySQL interpreter (mysql> prompt).
It's described in detail under this link (haven't tried it myself though, it's just in my bookmarks):
http://www.youdidwhatwithtsql.com/mysql-clone-of-sp_msforeachtable/624
DELIMITER $$
DROP PROCEDURE IF EXISTS `usp_mysql_foreachtable`$$
CREATE PROCEDURE `usp_mysql_foreachtable`(IN sql_string VARCHAR(1000))
LANGUAGE SQL
NOT DETERMINISTIC
COMMENT 'Functional clone of sp_MsForEachTable'
BEGIN
DECLARE var_tablename VARCHAR(100);
DECLARE last_row BIT;
DECLARE table_cursor CURSOR FOR SELECT TABLE_NAME
FROM information_schema.TABLES
WHERE TABLE_TYPE = 'BASE TABLE'
AND TABLE_SCHEMA = DATABASE();
DECLARE CONTINUE HANDLER FOR NOT FOUND SET last_row = 1;
OPEN table_cursor;
FETCH table_cursor INTO var_tablename;
SET last_row = 0;
SET #var = '';
lbl_table_cursor: LOOP
SET #qry = REPLACE(sql_string, '?', var_tablename);
PREPARE q FROM #qry;
EXECUTE q;
DEALLOCATE PREPARE q;
FETCH table_cursor INTO var_tablename;
IF last_row = 1 THEN
LEAVE lbl_table_cursor;
END IF;
END LOOP lbl_table_cursor;
CLOSE table_cursor;
END$$
DELIMITER ;
Then you call it with
CALL usp_mysql_foreachtable('SELECT * FROM ? LIMIT 3;');
Seen a lot for dropping tables using a wildcard but not a direct SQL statement except this one:
http://azimyasin.wordpress.com/2007/08/11/mysql-dropping-multiple-tables/
It says:
SHOW TABLES LIKE ‘phpbb_%’;
then DROP TABLES, is there a neat way to combine this all into one SQL Statement?
You could use dynamic SQL to do it, inside a stored procedure. It'd look something like this (untested):
CREATE PROCEDURE drop_like (IN pattern VARCHAR(64))
BEGIN
DECLARE q tinytext;
DECLARE done INT DEFAULT FALSE;
DECLARE cur CURSOR FOR
SELECT CONCAT('DROP TABLE "', table_schema, '"."', table_name, '"')
FROM information_schema.tables WHERE table_type = 'BASE TABLE' AND table_name LIKE pattern;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN cur;
drop_loop: LOOP
FETCH cur INTO q;
IF done THEN
LEAVE drop_loop;
END IF;
PREPARE stmt FROM #q;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END LOOP;
CLOSE cur;
END;
Using dynamic SQL in a query, as per derobert's answer, is the only to do this with pure SQL (no app code).
I wrote a generalized procedure to do this sort of thing (run a query for every table in a database) that you can find here - to use it, you would just need to run this query:
CALL p_run_for_each_table('databasename', 'DROP TABLE `{?database}`.`{?table}`');
It works in essentially the same way as derobert's answer.
However, the writer of that blog post was probably expecting you to write app code to turn the names of tables into a single DROP statement.
To do this, you would iterate over the results of the SHOW TABLE in your code and build a single query like this:
DROP TABLE table1, table2, tablewhatever;
This can be achieved via stored procedure, for example:
CREATE DEFINER=`some_user`#`%` PROCEDURE `drop_tables`()
LANGUAGE SQL
NOT DETERMINISTIC
MODIFIES SQL DATA
SQL SECURITY DEFINER
COMMENT ''
BEGIN
#We need to declare a variable with default 0 to determine weather to continue the loop or exit the loop.
DECLARE done INT DEFAULT 0;
DECLARE archive_table_name VARCHAR(100);
#Select desired tables from `information_schema`
DECLARE cur CURSOR FOR
SELECT t.`TABLE_NAME` FROM information_schema.`TABLES` t WHERE t.`TABLE_NAME` LIKE 'some_table_name%'
AND t.CREATE_TIME BETWEEN DATE_SUB(NOW(), INTERVAL 9 MONTH) AND DATE_SUB(NOW(), INTERVAL 6 MONTH);
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
OPEN cur;
read_loop: LOOP
#Fetch one record from CURSOR and set variable (if not found, then variable `done` will be set to 1 by continue handler)
FETCH cur INTO archive_table_name;
IF done THEN
LEAVE read_loop; #If done is set to 1, then exit the loop, else continue
END IF;
#Do your work
-- Create the truncate query
SET #s = CONCAT('DROP TABLE IF EXISTS ', archive_table_name);
-- Prepare, execute and deallocate the truncate query
PREPARE drop_statement FROM #s;
EXECUTE drop_statement;
DEALLOCATE PREPARE drop_statement;
END LOOP;
CLOSE cur; #Closing the cursor
END
Pay attention to the database user, which is creating/executing the stored routine: it must have appropriate credentials for executing/dropping tables.
I am trying to selectively flush a limited set of tables in a particular database - and after bit of investigation (swearing and cussing), I've arrived at:
DROP PROCEDURE IF EXISTS local_flush_cache;
DELIMITER $$
CREATE PROCEDURE local_flush_cache()
BEGIN
DECLARE done INT DEFAULT 0;
DECLARE a VARCHAR(64);
DECLARE crsr CURSOR FOR SELECT table_name FROM information_schema.tables WHERE table_schema=database() AND table_name LIKE 'cache_%';
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
OPEN crsr;
read_loop: LOOP
FETCH crsr INTO a;
IF done THEN
LEAVE read_loop;
END IF;
SET #s = CONCAT('DELETE FROM ', a);
PREPARE stmt FROM #s;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END LOOP;
CLOSE crsr;
END$$
DELIMITER ;
The only thing is - it doesn't seem to be working. When I check the contents of the cache tables, they've not been cleared.
When I call the procedure, the only output I get is:
mysql> call local_flush_cache();
Query OK, 0 rows affected (0.00 sec)
And then when I get a row count from one of the cache tables, I get a non-zero result.
Since you are setting the delimiter to $$ on the first line, you need to change the semicolon on the second line to $$ instead.
After taking a lunch break - the problem ended up being the query parameter for the select in the procedure:
DECLARE crsr CURSOR FOR SELECT table_name FROM information_schema.tables WHERE table_schema=database() AND table_name LIKE 'cache_%';
The table I was testing was actually called cache - so this would obviously not be matched by the parameter above. The line actually needs to be:
DECLARE crsr CURSOR FOR SELECT table_name FROM information_schema.tables WHERE table_schema=database() AND table_name LIKE 'cache%';
This works great now.