How to use a user variable in 'USE database' statement - mysql

I want to make the name of my MYSQL database variable, so it can be set from the outside (a python script) to whatever I like. Thus avoiding hardcoding the name of the database.
I tried passing the name via defining a user variable in the mysql client and then loading the sql script. I was able to create the database but not to connect to it via the USE statement
Definition of variable and calling the sql script:
mysql> SET #database_name = 'name';
mysql> SOURCE script.sql;
Code of script.sql:
-- creating database according to user variable
SET #query = CONCAT('CREATE SCHEMA IF NOT EXISTS ', #database_name, ' DEFAULT CHARACTER SET latin1');
PREPARE stmt FROM #query; EXECUTE stmt; DEALLOCATE PREPARE stmt;
-- selecting the database according to the user variable
SET #query = CONCAT('USE ', #database_name);
PREPARE stmt FROM #query; EXECUTE stmt; DEALLOCATE PREPARE stmt;
I expected that this would work, but unexpectedly MYSQL threw this error into my face:
This command is not supported in the prepared statement protocol yet
The problem is, that there are a lot of table defintions in this script, and they all need the actual database name in front of them, or the USE statement has to work, so the correct database is selected and no database name is needed when creating the tables.
I am grateful for any useful tips, thank you :).

A "complex" workaround might be to generate a .sql file if you have FILE privilege and the secure_file_priv system variable is set also max_allowed_packet also might to be changed.
# SET SESSION max_allowed_packet = '<>'; commented because it might be needed to generate larger .sql file
SET SESSION group_concat_max_len = ##max_allowed_packet;
SELECT
GROUP_CONCAT(sql_lines.line SEPARATOR ';');
FROM (
SELECT CONCAT('CREATE SCHEMA IF NOT EXISTS ', #database_name, 'DEFAULT CHARACTER SET latin1') AS line
UNION
SELECT CONCAT('USE ', #database_name) AS line
) AS sql_lines
CROSS JOIN (
SELECT #database_name = '<test>'
) AS init_user_param
INTO
DUMPFILE '<absolute_file_path>';
SOURCE script.sql;

There is a way, you can generate the sql like you were typing it on the sql prompt. You could write everything like you are typing it, but using a bash script:
Code like this (filename: generate-database.sh):
#!/bin/bash
mydatabase="mytest";
cat << EOF
create database $mydatabase;
use $mydatabase;
create table test1 (id int);
create table test2 (id int);
EOF
You can even pass the database name as the first parameter if you want to have a database for a different site. Then you would do something like this:
bash generate-database.sh | mysql -u <username> -p
Then that will do everything like you would do as if you were typing it in mysql.
To pass in as the first parameter you would do this:
mydatabase=$1;
On the command prompt you would do this:
bash generate-database.sh MarkTownsend | mysql -u <username> -p

Related

Use MySQL procedure to change the name of a table

I am trying to create a stored procedure to change the name of an existing table by appending the date to it.
I have only been using MySQL for a short while and cannot see why the code is not working. I'm using MySQL Workbench 8.0 Community to connect to the MySQL database and run the code. In Workbench no errors are returned when I run the code.
The code I have managed to find so far is:
DELIMITER \\
DROP PROCEDURE IF EXISTS `usp_test_dynamic_sql`\\
CREATE PROCEDURE `usp_test_dynamic_sql`()
BEGIN
SET #s = concat('ALTER TABLE MyDashboardTable RENAME TO MyDashboardTable_',replace(date(now()),'-',''));
PREPARE stmt1 FROM #s;
EXECUTE stmt1;
END\\
DELIMITER ;
I have run part of the code, to find what #s is being set to by running:
SET #s = concat('ALTER TABLE MyDashboardTable RENAME TO MyDashboardTable_',replace(date(now()),'-',''));
select #s;
I have then pasted the result for #s
ALTER TABLE MyDashboardTable RENAME TO MyDashboardTable_20200904
This does change the name of the table but I cannot figure out why the stored procedure does not. Any help will be greatly appreciated.
Execute your SP as
CALL usp_test_dynamic_sql();
You may also want to modify your
replace(date(now()),'-','')
with
DATE_FORMAT(NOW(),'%Y%m%d')
Even though it's not absolutely necessary. If you do, do not leave spaces or use '-' or '/' between the formats since MySQL with throw an error.

Database name as variable in MySQL query

I am creating a MySQL dump file, that needs to run with multiple databases.
The structure is almost like this :
SET #parent_database = 'db_name';
CREATE OR REPLACE VIEW t_colours AS
SELECT `key_name`, `value`
FROM #parent_database. `colours` as COLOURS WHERE 1;
When I run this query next time, my plan is to only change the variable parent_database.
Is this possible?
Everytime now I run this, I receive an error :
#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 '#parent_database.`colours` as COLOURS WHERE 1' at line 1
Any way to make this happen?
There are many more views to create.
When you execute the queries the mysql thinks of table names as objects and not as string, and hence this would not work as you would expect.
However, there is a way out and you could use something similar to below snippet.
SET #parent_database = 'db_name';
SET #q = CONCAT('CREATE OR REPLACE VIEW t_colours AS SELECT `key_name`,`value` FROM', #parent_database, '.`colours` as COLOURS WHERE 1;');
PREPARE stmt FROM #q;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

How can I drop all MySQL Databases with a certain prefix?

I need to drop hundreds of mysql databases that all share a common prefix, but have random id's as the rest of their name ( eg. database_0123456789, database_9876543210 ). All these databases are on the same server. There are other databases on that same server that I don't want to drop.
This is what I'd like to do:
DROP DATABASE `database_*`;
How can I drop these efficiently? Is there a MySQL query I can run? Maybe a shell script?
The syntax of the DROP DATABASE statement supports only a single database name. You will need to execute a separate DROP DATABASE statement for each database.
You can run a query to return a list of database names, or maybe more helpful, to generate the actual statements you need to run. If you want to drop all databases that start with the literal string database_ (including the underscore character), then:
SELECT CONCAT('DROP DATABASE `',schema_name,'` ;') AS `-- stmt`
FROM information_schema.schemata
WHERE schema_name LIKE 'database\_%' ESCAPE '\\'
ORDER BY schema_name
Copy the results from that query, and you've got yourself a SQL script.
(Save the results as plain text file (e.g. dropdbs.sql), review with your favorite text editor to remove any goofy header and footer lines, make sure the script looks right, save it, and then from the mysql command line tool, mysql> source dropdbs.sql.)
Obviously, you could get more sophisticated than that, but for a one-time shot, this is probably the most efficient.)
Don't need of an external script file. A stored procedure using prepare statements might do the trick:
CREATE PROCEDURE kill_em_all()
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE dbname VARCHAR(255);
DECLARE cur CURSOR FOR SELECT schema_name
FROM information_schema.schemata
WHERE schema_name LIKE 'database\_%' ESCAPE '\\'
ORDER BY schema_name;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN cur;
read_loop: LOOP
FETCH cur INTO dbname;
IF done THEN
LEAVE read_loop;
END IF;
SET #query = CONCAT('DROP DATABASE ',dbname);
PREPARE stmt FROM #query;
EXECUTE stmt;
END LOOP;
END;
Once you have that procedure, you just have to:
CALL kill_em_all();
When done:
DROP PROCEDURE kill_em_all
This question lacks an answer without creating a file first.
Our build server automatically creates a database for every topic branch while running unit tests. After information_schema queries get really slow which causes our tests to fail.
I created a batch file which runs every day. I did not want to deal with temporary files. So here is my solution.
#ECHO OFF
REM drops all databases excluding defaults
SET user=user
SET pass=pass
mysql ^
-u %user% ^
-p%pass% ^
-NBe "SELECT CONCAT('drop database `', schema_name, '`;') from information_schema.schemata where schema_name NOT IN ('mysql', 'test', 'performance_schema', 'information_schema')" | mysql -u %user% -p%pass%
Modifying spencer7593 answer
Here is the command to find desired results and save it in file where prefix is database prefix
SELECT CONCAT('DROP DATABASE ',schema_name,' ;') AS stmt
FROM information_schema.schemata
WHERE schema_name LIKE 'prefix\_%' ESCAPE '\\'
ORDER BY schema_name into outfile '/var/www/pardeep/file.txt';
if you get permission denied then change folder permission to 777 or change folder group to mysql using this
chown -R mysql /var/www/pardeep/
then run this query
source /var/www/pardeep/file.txt;

How to append data from SQL to an existing file

SQL has the option to dump data into a file, using the INTO OUTFILE option, for exmaple
SELECT * from FIshReport INTO OUTFILE './FishyFile'
The problem is, this command is only allowed if the file didn't exist before it. It creates the file and then enters the data.
So, is there any way to append data to a file this way?
As the MySQL page on SELECT syntax suggests:
http://dev.mysql.com/doc/refman/5.0/en/select.html
the alternative to this is to issue the SELECT from the MySQL client:
However, if the MySQL client software is installed on the remote machine,
you can instead use a client command such as mysql -e "SELECT ..." > file_name
to generate the file on the client host.
which, in your case, would be modified to be:
mysql -e "SELECT * from FishReport" >> file_name
so that you simply append to the file.
From your Tcl script, you could simply issue this as an exec command:
http://www.tcl.tk/man/tcl/tutorial/Tcl26.html
I think MySQL does not allow appending data to an existing file or overwriting an existing file for security reasons.
A work around could be to save resuts in seperate files and then append the using file IO.
You could always append the output from your SQL script to a file using >>
For example (for Sybase):
isql < script.sql >> outputfile.out
I can't tell you what the equivalent is for MySQL but the principle should be the same.
Of course output will all go to one file so if your SQL script is outputting various SQL selects to different output files then you'd need to split the script up.
You could just add it to a variable. Then use a SELECT with UNION.
declare t varchar(100);
set #myvar = concat('
select * INTO OUTFILE \'',file,'\'
from (
select \'',t,'\'
union all
SELECT col from tbl where x
) a'
);
PREPARE stmt1 FROM #myvar;
EXECUTE stmt1;
Deallocate prepare stmt1;

How do I pass a variable to a mysql script?

I know that with mysql you can write SQL statements into a .sql file and run the file from the mysql command line like this:
mysql> source script.sql
How do I pass a variable to the script? For example, if I want to run a script that retrieves all the employees in a department, I want to be able to pass in the number of the department as a variable.
I am not trying to run queries through a shell script. There are simple queries I run from the mysql command line. I'm tired of retyping them all the time, and writing a shell script for them would be overkill.
#!/bin/bash
#verify the passed params
echo 1 cmd arg : $1
echo 2 cmd arg : $2
export db=$1
export tbl=$2
#set the params ... Note the quotes ( needed for non-numeric values )
mysql -uroot -pMySecretPaassword \
-e "set #db='${db}';set #tbl='${tbl}';source run.sql ;" ;
#usage: bash run.sh my_db my_table
#
#eof file: run.sh
--file:run.sql
SET #query = CONCAT('Select * FROM ', #db , '.' , #tbl ) ;
SELECT 'RUNNING THE FOLLOWING query : ' , #query ;
PREPARE stmt FROM #query;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
--eof file: run.sql
you can re-use the whole concept from from the following project
Like this:
set #department := 'Engineering';
Then, reference #department wherever you need to in script.sql:
update employee set salary = salary + 10000 where department = #department;
you really should be looking at a more appropriate way of doing this. i'm going to guess that you're trying to run mysql queries via a shell script. you should instead be using something like PERL or PHP.