How to feed mysql queries from bash - mysql

I'm trying to make a bash script that creates a mysql user and database but I can't find a way to feed the sql into mysql, I'm trying with this format:
mysql < echo "query"
But that is not working, see the example below:
mysql --host=localhost --user=user --password=password < echo "CREATE USER 'testuser'#'localhost' IDENTIFIED BY 'jakdJxct8W';
CREATE DATABASE IF NOT EXISTS 'testuser_dev' DEFAULT CHARACTER SET utf8 COLLATE utf8_bin;
GRANT ALL PRIVILEGES ON 'testuser_dev' . * TO 'testuser'#'localhost';
CREATE DATABASE IF NOT EXISTS 'testuser_qa' DEFAULT CHARACTER SET utf8 COLLATE utf8_bin;
GRANT ALL PRIVILEGES ON 'testuser_qa' . * TO 'testuser'#'localhost';"
How to feed mysql with the queries?

Try like this:
echo "select 1" | mysql

Try using a here document like this:
mysql --host=localhost --user=user --password=password << END
CREATE USER 'testuser'#'localhost' IDENTIFIED BY 'jakdJxct8W';
CREATE DATABASE IF NOT EXISTS 'testuser_dev' DEFAULT CHARACTER SET utf8 COLLATE utf8_bin;
GRANT ALL PRIVILEGES ON 'testuser_dev' . * TO 'testuser'#'localhost';
CREATE DATABASE IF NOT EXISTS 'testuser_qa' DEFAULT CHARACTER SET utf8 COLLATE utf8_bin;
GRANT ALL PRIVILEGES ON 'testuser_qa' . * TO 'testuser'#'localhost';
END
Alternatively place all you commands in text file and run it:
mysql --host=localhost --user=user --password=password < commands.sql

mysql --batch --silent -e 'SHOW TABLES';
Batch and silent are handy if you are planning to pipe the output

The reason your attempt did not work was because the < expects a file name and you fed it a string. You would have to do something like
echo "YOURQUERYSTRINGHERE">tmpfile
mysql --host=localhost --user=user --password=password dbname <tmpfile
ken's suggestion of
mysql --host=localhost --user=user --password=password -e "QUERY" dbname
can work, but if you try to use bash variables in your query you can fall foul of parameter expansion. eg
QUERY="select * from $MYTABLE WHERE name=\"silly#place.com\";"
mysql --host=localhost --user=user --password=password -e "$QUERY" mydbname
may not do what you expect.
One option is use
echo "$QUERY"|mysql --host=localhost --user=user --password=password mydbname
which works if the query string contains appropriate quoting. Another option is the "here" document as suggested by dogbane.

Have you tried mysql -e query?

cat <<EOD | mysql [-u user] [-ppassword] [database]
select 1;
select 2;
select 3;
EOD
in your case
cat <<EOD | mysql -u root -p
CREATE DATABASE IF NOT EXISTS testuser_dev DEFAULT CHARACTER SET utf8 COLLATE utf8_bin;
GRANT ALL PRIVILEGES ON testuser_dev.* TO "testuser"#"localhost";
CREATE DATABASE IF NOT EXISTS testuser_qa DEFAULT CHARACTER SET utf8 COLLATE utf8_bin;
GRANT ALL PRIVILEGES ON testuser_qa.* TO "testuser"#"localhost";
FLUSH PRIVILEGES;
EOD

For big queries in a bash script, you can try:
read -d '' SQL_QUERY_1 << EOF
SELECT prod.id as id, prod.title as title, comp.name as company, pho.id as photo_id, pho.image as photo_name
FROM products as prod
JOIN accounts as comp
ON comp.id = prod.account_id
JOIN photos as pho
ON pho.id = prod.featured_photo_id;
EOF
echo ${SQL_QUERY_1} | mysql

Related

Run Query to MySQL from a Bash Script

please help, whats wrong?
sudo -u root /etc/scripts/mysql.sh root 111111
#!/bin/bash
mysql --host=localhost --user=root --password=111111
mysql 1<< EOF
INSERT INTO
table1(id)
SELECT MAX(id) + 1 FROM table1;
EOF
You should just invoke MySQL once and specify the default database:
#!/bin/bash
mysql --host=localhost --user=root --password=111111 1 << EOF
INSERT INTO
table1(id)
SELECT MAX(id) + 1 FROM table1;
EOF
Use below script:
#!/bin/bash
USER='root'
PASS='root123'
mysql -u$USER -p$PASS mydb -e"insert into table1 (id) select (max(id) + 1) from table1;"
Note: As you are executing sql from same server so no need of localhost.
Now use as from directory where your script exists-
sh myscript.sh

Insert timestamp into mysql column in shell

The following works for me:
mysql -u 'root' -h 8.8.8.88 mo -e 'UPDATE `path_last_updated`
SET timestamp="2012-01-03 00:00:00"'
However, the following does not:
TIMESTAMP=`date "+%Y-%m-%d-%T"`
mysql -u 'root' -h 8.8.8.88 mo -e 'UPDATE `path_last_updated`
SET timestamp=$TIMESTAMP'
How would I insert the timestamp from unix into my mysql table?
Update:
TIMESTAMP=`date "+%Y-%m-%d %T"`
mysql -u 'root' -h 8.8.8.88 mo -e "UPDATE `path_last_updated`
SET timestamp='$TIMESTAMP'"
ERROR 1064 (42000) at line 1: 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 'SET
timestamp='2013-01-31 15:46:00'' at line 1
Shell variable interpolation only works between double quotes ("), not single ('). You've also got backticks in there, which in a double-quoted string will be treated as an embedded shell command.
Try:
mysql -u 'root' -h 8.8.8.88 mo -e "UPDATE \`path_last_updated\`
SET timestamp='$TIMESTAMP'"
Also, fwiw, you have an extra dash (-) in your format for the date command, between the %d and %T.
ALTER TABLE to make it easier:
ALTER TABLE path_last_updated ADD date_entered timestamp DEFAULT CURRENT_TIMESTAMP
And then in shell:
mysql -u 'root' -h 8.8.8.88 mo -e "UPDATE path_last_updated SET timestamp=DEFAULT"

How to convert all tables in database to one collation?

I'm getting error:
Illegal mix of collations (utf8_general_ci,IMPLICIT) and (utf8_unicode_ci,IMPLICIT) for operation '='"
I tried changing both tables manually to utf8_general_ci,IMPLICIT but I'm still getting the error.
Is there a way to convert all tables to utf8_general_ci,IMPLICIT and be done with it?
You need to execute a alter table statement for each table. The statement would follow this form:
ALTER TABLE tbl_name
[[DEFAULT] CHARACTER SET charset_name]
[COLLATE collation_name]
Now to get all the tables in the database you would need to execute the following query:
SELECT *
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA="YourDataBaseName"
AND TABLE_TYPE="BASE TABLE";
So now let MySQL write the code for you:
SELECT CONCAT("ALTER TABLE ", TABLE_SCHEMA, '.', TABLE_NAME," COLLATE your_collation_name_here;") AS ExecuteTheString
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA="YourDatabaseName"
AND TABLE_TYPE="BASE TABLE";
You can copy the results and execute them. I have not tested the syntax but you should be able to figure out the rest. Think of it as a little exercise.
Better option to change also collation of varchar columns inside table also
SELECT CONCAT('ALTER TABLE `', TABLE_NAME,'` CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci;') AS mySQL
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA= "myschema"
AND TABLE_TYPE="BASE TABLE"
Additionnaly if you have data with forein key on non utf8 column before launch the bunch script use
SET foreign_key_checks = 0;
It means global SQL will be for mySQL :
SET foreign_key_checks = 0;
ALTER TABLE `table1` CONVERT TO CHARACTER SET utf8 COLLATE utf8_unicode_ci;
ALTER TABLE `table2` CONVERT TO CHARACTER SET utf8 COLLATE utf8_unicode_ci;
ALTER TABLE `tableXXX` CONVERT TO CHARACTER SET utf8 COLLATE utf8_unicode_ci;
SET foreign_key_checks = 1;
But take care if according mysql documentation http://dev.mysql.com/doc/refman/5.1/en/charset-column.html,
If you use ALTER TABLE to convert a column from one character set to another, MySQL attempts to map the data values, but if the character sets are incompatible, there may be data loss. "
EDIT: Specially with column type enum, it just crash completly enums set (even if there is no special caracters)
https://bugs.mysql.com/bug.php?id=26731
#Namphibian's suggestion helped me a lot...
went a little further though and added columns and views to the script
just enter your schema's name below and it will do the rest
-- set your table name here
SET #MY_SCHEMA = "";
-- tables
SELECT DISTINCT
CONCAT("ALTER TABLE ", TABLE_NAME," CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;") as queries
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA=#MY_SCHEMA
AND TABLE_TYPE="BASE TABLE"
UNION
-- table columns
SELECT DISTINCT
CONCAT("ALTER TABLE ", C.TABLE_NAME, " CHANGE ", C.COLUMN_NAME, " ", C.COLUMN_NAME, " ", C.COLUMN_TYPE, " CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;") as queries
FROM INFORMATION_SCHEMA.COLUMNS as C
LEFT JOIN INFORMATION_SCHEMA.TABLES as T
ON C.TABLE_NAME = T.TABLE_NAME
WHERE C.COLLATION_NAME is not null
AND C.TABLE_SCHEMA=#MY_SCHEMA
AND T.TABLE_TYPE="BASE TABLE"
UNION
-- views
SELECT DISTINCT
CONCAT("CREATE OR REPLACE VIEW ", V.TABLE_NAME, " AS ", V.VIEW_DEFINITION, ";") as queries
FROM INFORMATION_SCHEMA.VIEWS as V
LEFT JOIN INFORMATION_SCHEMA.TABLES as T
ON V.TABLE_NAME = T.TABLE_NAME
WHERE V.TABLE_SCHEMA=#MY_SCHEMA
AND T.TABLE_TYPE="VIEW";
Below is the more accurate query.
I am giving example how to convert it to utf8
SELECT CONCAT("ALTER TABLE `", TABLE_NAME,"` DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci;") AS mySQL
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA="myschema"
AND TABLE_TYPE="BASE TABLE"
If you're using PhpMyAdmin, you can now:
Select the database.
Click the "Operations" tab.
Under "Collation" section, select the desired collation.
Click the "Change all tables collations" checkbox.
A new "Change all tables columns collations" checkbox will appear.
Click the "Change all tables columns collations" checkbox.
Click the "Go" button.
I had over 250 tables to convert. It took a little over 5 minutes.
You can use this BASH script:
#!/bin/bash
USER="YOUR_DATABASE_USER"
PASSWORD="YOUR_USER_PASSWORD"
DB_NAME="DATABASE_NAME"
CHARACTER_SET="utf8" # your default character set
COLLATE="utf8_general_ci" # your default collation
tables=`mysql -u $USER -p$PASSWORD -e "SELECT tbl.TABLE_NAME FROM information_schema.TABLES tbl WHERE tbl.TABLE_SCHEMA = '$DB_NAME' AND tbl.TABLE_TYPE='BASE TABLE'"`
for tableName in $tables; do
if [[ "$tableName" != "TABLE_NAME" ]] ; then
mysql -u $USER -p$PASSWORD -e "ALTER TABLE $DB_NAME.$tableName DEFAULT CHARACTER SET $CHARACTER_SET COLLATE $COLLATE;"
echo "$tableName - done"
fi
done
For phpMyAdmin I figured this out:
SELECT GROUP_CONCAT("ALTER TABLE ", TABLE_SCHEMA, '.', TABLE_NAME," CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;" SEPARATOR ' ') AS OneSQLString
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA="yourtableschemaname"
AND TABLE_TYPE="BASE TABLE"
Just change yourtableschemaname and you're fine.
Taking the answer from #Petr Stastny a step further by adding a password variable. I'd prefer if it actually took it in like a regular password rather than as an argument, but it's working for what I needed.
#!/bin/bash
# mycollate.sh <database> <password> [<charset> <collation>]
# changes MySQL/MariaDB charset and collation for one database - all tables and
# all columns in all tables
DB="$1"
PW="$2"
CHARSET="$3"
COLL="$4"
[ -n "$DB" ] || exit 1
[ -n "$PW" ]
[ -n "$CHARSET" ] || CHARSET="utf8mb4"
[ -n "$COLL" ] || COLL="utf8mb4_bin"
PW="--password=""$PW"
echo $DB
echo "ALTER DATABASE $DB CHARACTER SET $CHARSET COLLATE $COLL;" | mysql -u root "$PW"
echo "USE $DB; SHOW TABLES;" | mysql -s "$PW" | (
while read TABLE; do
echo $DB.$TABLE
echo "ALTER TABLE $TABLE CONVERT TO CHARACTER SET $CHARSET COLLATE $COLL;" | mysql "$PW" $DB
done
)
PW="pleaseEmptyMeNow"
This is my version of a bash script. It takes database name as a parameter and converts all tables to another charset and collation (given by another parameters or default value defined in the script).
#!/bin/bash
# mycollate.sh <database> [<charset> <collation>]
# changes MySQL/MariaDB charset and collation for one database - all tables and
# all columns in all tables
DB="$1"
CHARSET="$2"
COLL="$3"
[ -n "$DB" ] || exit 1
[ -n "$CHARSET" ] || CHARSET="utf8mb4"
[ -n "$COLL" ] || COLL="utf8mb4_general_ci"
echo $DB
echo "ALTER DATABASE $DB CHARACTER SET $CHARSET COLLATE $COLL;" | mysql
echo "USE $DB; SHOW TABLES;" | mysql -s | (
while read TABLE; do
echo $DB.$TABLE
echo "ALTER TABLE $TABLE CONVERT TO CHARACTER SET $CHARSET COLLATE $COLL;" | mysql $DB
done
)
Following on from G H I've added the user and host parameters incase you need to do this on a remote server
#!/bin/bash
# mycollate.sh <database> <user> <password> [<host> <charset> <collation>]
# changes MySQL/MariaDB charset and collation for one database - all tables and
# all columns in all tables
DB="$1"
USER="$2"
PW="$3"
HOST="$4"
CHARSET="$5"
COLL="$6"
[ -n "$DB" ] || exit 1
[ -n "$USER" ] || exit 1
[ -n "$PW" ] || exit 1
[ -n "$HOST" ] || HOST="localhost"
[ -n "$CHARSET" ] || CHARSET="utf8mb4"
[ -n "$COLL" ] || COLL="utf8mb4_general_ci"
PW="--password=""$PW"
HOST="--host=""$HOST"
USER="--user=""$USER"
echo $DB
echo "ALTER DATABASE $DB CHARACTER SET $CHARSET COLLATE $COLL;" | mysql "$HOST" "$USER" "$PW"
echo "USE $DB; SHOW TABLES;" | mysql "$HOST" "$USER" "$PW" | (
while read TABLE; do
echo $DB.$TABLE
echo "ALTER TABLE $TABLE CONVERT TO CHARACTER SET $CHARSET COLLATE $COLL;" | mysql "$HOST" "$USER" "$PW" $DB
done
)
PW="pleaseEmptyMeNow"
If you want a copy-paste bash script:
var=$(mysql -e 'SELECT CONCAT("ALTER TABLE ", TABLE_NAME," CONVERT TO CHARACTER SET utf8 COLLATE utf8_czech_ci;") AS execTabs FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA="zabbix" AND TABLE_TYPE="BASE TABLE"' -uroot -p )
var+='ALTER DATABASE zabbix CHARACTER SET utf8 COLLATE utf8_general_ci;'
echo $var | cut -d " " -f2- | mysql -uroot -p zabbix
Change zabbix to your database name.
I will share my answer using MySQL procedure.
You need to run 3 sql command.
1.
DROP PROCEDURE IF EXISTS UpdateTable;
2.
DELIMITER $$
CREATE PROCEDURE UpdateTable()
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE _table_name CHAR(255);
DECLARE cur CURSOR FOR
SELECT table_name FROM information_schema.tables
WHERE table_schema = 'my_db_name' AND table_type = "BASE TABLE";
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN cur;
My_loop: LOOP
FETCH cur INTO _table_name;
SET #my_table_name = _table_name;
IF done THEN
LEAVE My_loop;
END IF;
SET FOREIGN_KEY_CHECKS = 0;
SET #stmt = CONCAT('ALTER TABLE ', #my_table_name, ' CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci;');
PREPARE stmt1 FROM #stmt;
EXECUTE stmt1;
DEALLOCATE PREPARE stmt1;
SET FOREIGN_KEY_CHECKS = 1;
END LOOP;
CLOSE cur;
END$$
DELIMITER ;
3.
CALL UpdateTable();
Then run first one again. If you don't want to store the procedure.

Bash select with string return

I'm executing the following query, to update the data in my database:
mysql -u root -ppassword -e "UPDATE table SET data = 1 WHERE id = 5"
Is there a way to execute the same query, but with the SELECT statement ? Of course there is, but how can I fetch the returned value then? Is that even possible?
mysql -u root -ppassword -e "UPDATE table SET data = 1 WHERE id = 5; SELECT data FROM table WHERE id = 5"

How to echo print statements while executing a sql script

We have a simple sql script which needs to be executed against a MySQL database and we would like print log statements on the progress of the script (e.g. Inserted 10 records into foo or Deleted 5 records from bar). How do we do this?
I would like to know the syntax to be used for insert/update/delete statements.
How do I know about the number of rows affected by my statement(s).
I would also like to control printing them using a ECHO off or on command at the top of the script.
The script should be portable across Windows / Linux OS.
This will give you are simple print within a sql script:
select 'This is a comment' AS '';
Alternatively, this will add some dynamic data to your status update if used directly after an update, delete, or insert command:
select concat ("Updated ", row_count(), " rows") as '';
I don't know if this helps:
suppose you want to run a sql script (test.sql) from the command line:
mysql < test.sql
and the contents of test.sql is something like:
SELECT * FROM information_schema.SCHEMATA;
\! echo "I like to party...";
The console will show something like:
CATALOG_NAME SCHEMA_NAME DEFAULT_CHARACTER_SET_NAME
def information_schema utf8
def mysql utf8
def performance_schema utf8
def sys utf8
I like to party...
So you can execute terminal commands inside an sql statement by just using \!, provided the script is run via a command line.
\! #terminal_commands
Just to make your script more readable, maybe use this proc:
DELIMITER ;;
DROP PROCEDURE IF EXISTS printf;
CREATE PROCEDURE printf(thetext TEXT)
BEGIN
select thetext as ``;
END;
;;
DELIMITER ;
Now you can just do:
call printf('Counting products that have missing short description');
What about using mysql -v to put mysql client in verbose mode ?
For mysql you can add \p to the commands to have them print out while they run in the script:
SELECT COUNT(*) FROM `mysql`.`user`
\p;
Run it in the MySQL client:
mysql> source example.sql
--------------
SELECT COUNT(*) FROM `mysql`.`user`
--------------
+----------+
| COUNT(*) |
+----------+
| 24 |
+----------+
1 row in set (0.00 sec)
You can use print -p -- in the script to do this example :
#!/bin/ksh
mysql -u username -ppassword -D dbname -ss -n -q |&
print -p -- "select count(*) from some_table;"
read -p get_row_count1
print -p -- "select count(*) from some_other_table;"
read -p get_row_count2
print -p exit ;
#
echo $get_row_count1
echo $get_row_count2
#
exit