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
Related
I have to send comma separated values into a select statement where it will update values through #sql statement.
I have common table in all Databases I need to update the table column by one update statement in the procedure.
For Example : Input Param will be ('DataBase1','Database2',....., 'Database10')
Below is the sample procedure :
DELIMITER &&
CREATE PROCEDURE update_stmt (IN DBName varchar(100))
BEGIN
Declare DBName = #DB;
**comma seperated values loop and placed into the #DB**
use #DB;
SELECT concat(update #DB.sample SET COL = 0 where ID = \'',ID,'\','; ) as stmt FROM
Test.Sample into #s;
SET #sql = #s
PREPARE stmt from #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END &&
DELIMITER ;
so that update statement will execute in each of the databases.
Here's another approach. I don't try to split the comma-separated string, I use it with FIND_IN_SET() to match schema names in INFORMATION_SCHEMA.TABLES. This filters to schemas in the list that actually exist, and tables that actually exist in that schema.
Then use a cursor to loop over the matching rows, so you don't have to split any strings, which is awkward to do in a stored procedure.
I supposed that you would want to specify the id of the row to update too, so I added that to the procedure parameters.
Also notice the use of quotes when I create #sql. You can concatenate strings, but those must be quote-delimited like any other string literal. Variables must not be inside the quoted string. There's no feature to expand variables inside string literals in MySQL.
DELIMITER &&
CREATE PROCEDURE update_stmt (IN schema_name_list VARCHAR(100), IN in_id INT)
BEGIN
DECLARE done INT DEFAULT false;
DECLARE schema_name VARCHAR(64);
DECLARE cur1 CURSOR FOR
SELECT TABLE_SCHEMA FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME = 'sample' AND FIND_IN_SET(TABLE_SCHEMA, schema_name_list);
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = true;
SET #id = in_id;
OPEN cur1;
schema_loop: LOOP
FETCH cur1 INTO schema_name;
IF done THEN
LEAVE schema_loop;
END IF;
SET #sql = CONCAT('UPDATE `', schema_name, '`.sample SET col = 0 WHERE id = ?');
PREPARE stmt FROM #sql;
EXECUTE stmt USING #id;
DEALLOCATE PREPARE stmt;
END LOOP;
CLOSE cur1;
END &&
DELIMITER ;
Frankly, I hardly ever use stored procedures in MySQL. The procedure language is primitive, and the tasks I see people try to do in stored procedures could be done a lot more easily in virtually every other programming language.
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).
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.
I have a joomla mysql database with a table name prefix of "jos_" on all of my table names. But I would like to remove it from all of my tables. I understand how to rename each table, one at a time, but I have 600 tables. Is there an easy to run a sql query to do this.
If someone has a solution, could you please post the exact sql query I can use?
In phpmyadmin select all tables of your database.
From the dropdown 'With selected:' choose 'Replace table prefix'
Set from->to replacement.
DONE
You can generate the necessary statements with a single query:
select 'RENAME TABLE ' || table_name || ' TO ' || substr(table_name, 5) ||';'
from information_schema.tables
Save the output of that query to a file and you have all the statements you need.
Or if that returns 0s and 1s rather the statemenets, here's the version using concat instead:
select concat('RENAME TABLE ', concat(table_name, concat(' TO ', concat(substr(table_name, 5), ';'))))
from information_schema.tables;
You can create your own stored procedure to rename your tables, with that you don't need to open an external editor everything will be done on the server:
delimiter //
CREATE PROCEDURE rename_tables( IN db CHAR(255), IN srch CHAR(255), IN rplc CHAR(255) )
BEGIN
DECLARE done INT DEFAULT 0;
DECLARE from_table CHAR(255);
DECLARE cur1 CURSOR FOR SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA=db;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
OPEN cur1;
read_loop: LOOP
IF done THEN
LEAVE read_loop;
END IF;
FETCH cur1 INTO from_table;
SET #to_table = REPLACE(from_table, srch, rplc);
IF from_table != #to_table THEN
SET #rename_query = CONCAT('RENAME TABLE ', db, '.', from_table, ' TO ', #to_table, ';');
PREPARE stmt FROM #rename_query;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END IF;
END LOOP;
CLOSE cur1;
END//
delimiter ;
Usage:
CALL rename_tables('test', 'jos_', '');
Update: This was my first MySQL stored procedure and I ran into the 6 years old bug #5967 which was quite annoying, your variable names must be different from the field names, because if they aren't you'll get NULL values in your variables.
So be aware of that if you decide to write a MySQL stored procedure.
I need to use a variable to indicate what database to query in the declaration of a cursor. Here is a short snippet of the code :
CREATE PROCEDURE `update_cdrs_lnp_data`(IN dbName VARCHAR(25), OUT returnCode SMALLINT)
cdr_records:BEGIN
DECLARE cdr_record_cursor CURSOR FOR
SELECT cdrs_id, called, calling FROM dbName.cdrs WHERE lrn_checked = 'N';
# Setup logging
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
#call log_debug('Got exception in update_cdrs_lnp_data');
SET returnCode = -1;
END;
As you can see, I'm TRYING to use the variable dbName to indicate in which database the query should occur within. However, MySQL will NOT allow that. I also tried things such as :
CREATE PROCEDURE `update_cdrs_lnp_data`(IN dbName VARCHAR(25), OUT returnCode SMALLINT)
cdr_records:BEGIN
DECLARE cdr_record_cursor CURSOR FOR
SET #query = CONCAT("SELECT cdrs_id, called, calling FROM " ,dbName, ".cdrs WHERE lrn_checked = 'N' ");
PREPARE STMT FROM #query;
EXECUTE STMT;
# Setup logging
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
#call log_debug('Got exception in update_cdrs_lnp_data');
SET returnCode = -1;
END;
Of course this doesn't work either as MySQL only allows a standard SQL statement in the cursor declaration.
Can anyone think of a way to use the same stored procedure in multiple databases by passing in the name of the db that should be affected?
The answer of Vijay Jadhav is the right way to solve this limitation by MySQL. Actually, you need 3 proc to accomplish it:
proc1 using Vijay Jadhav's way, works like a data collector. You need to pass the variables to proc1 and let it create the tmp table for proc2. There is one limiation of Vijay's way, he should create a TEMPORARY table by using "CREATE TEMPORARY TABLE tmp_table_name SELECT ...". Because temporary table is thread safe.
proc2 declare the cursor on the tmp table which is created by proc1. Since the tmp table is already known and hard coded into the declaration, no more "table not found" error.
proc3 works like a "main" function, with all the parameters need to be sent to proc1 and proc2. proc3 simply calls proc1 first and then proc2 with the parameters need by each proc.
p.s Need to set system variable "sql_notes" to 0, otherwise proc1 will stop on DROP TABLE command.
Here is my example:
CREATE PROCEDURE `proc1`(SourceDBName CHAR(50), SourceTableName CHAR(50))
BEGIN
DECLARE SQLStmt TEXT;
SET #SQLStmt = CONCAT('DROP TEMPORARY TABLE IF EXISTS tmp_table_name');
PREPARE Stmt FROM #SQLStmt;
EXECUTE Stmt;
DEALLOCATE PREPARE Stmt;
SET #SQLStmt = CONCAT('CREATE TEMPORARY TABLE tmp_table_name SELECT ... FROM ',SourceDBName,'.',SourceTableName,' WHERE ... ');
PREPARE Stmt FROM #SQLStmt;
EXECUTE Stmt;
DEALLOCATE PREPARE Stmt;
END$$
CREATE PROCEDURE `proc2`(TargetDBName CHAR(50), TargetTemplateTableName CHAR(50))
BEGIN
DECLARE done INT DEFAULT 0;
DECLARE FieldValue CHAR(50);
DECLARE CursorSegment CURSOR FOR SELECT ... FROM tmp_table_name;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
OPEN CursorSegment;
REPEAT
FETCH CursorSegment INTO FieldValue;
IF NOT done THEN
...
END IF;
UNTIL done END REPEAT;
CLOSE CursorSegment;
END$$
CREATE PROCEDURE `proc3`(SourceDBName CHAR(50), SourceTableName CHAR(50), TargetDBName CHAR(50), TargetTemplateTableName CHAR(50))
BEGIN
CALL proc1(SourceDBName, SourceTableName);
CALL proc2(TargetDBName, TargetTemplateTableName);
END$$
No, you can't do that in cursors.
Maybe just prepared statements may do the job? :
delimiter ;;
create procedure test(in dbName varchar(40))
begin
set #query := CONCAT("SELECT * FROM " , dbName, ".db;");
PREPARE s from #query;
EXECUTE s;
DEALLOCATE PREPARE s;
end;;
delimiter ;
call test("mysql");
Try to create (temporary) table using prepared statement in a different procedure.
SET #query = CONCAT("CREATE TABLE temp_table AS SELECT cdrs_id, called, calling FROM " ,dbName, ".cdrs WHERE lrn_checked = 'N' ");
...
And then select data from that table in your 'test' procedure.
The answer to this is that it cannot be done. You cannot use variables in the cursor declaration. I appreciate noonex's response. However, his solution does not allow me to walk through the results. It simply executes the query.
create procedure test(in dbName varchar(40))
READS SQL DATA <- this line returns will allow you to walk through the results
begin
...
$result = call test("mysql");