有没有一种简单的方法可以从Linux命令行运行MySQL查询并以CSV 格式输出结果?
这就是我现在正在做的事情:
mysql -u uid -ppwd -D dbname << EOQ | sed -e 's/ /,/g' | tee list.csv
select id, concat("\"",name,"\"") as name
from students
EOQ
当有很多列需要用引号括起来,或者结果中有引号需要转义时,它会变得混乱。
来自http://www.tech-recipes.com/rx/1475/save-mysql-query-results-into-a-text-or-csv-file/
SELECT order_id,product_name,qty
FROM orders
WHERE foo = 'bar'
INTO OUTFILE '/var/lib/mysql-files/orders.csv'
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
LINES TERMINATED BY '\n';
使用此命令将不会导出列名称。
另请注意,/var/lib/mysql-files/orders.csv
将位于运行MySQL的服务器上。运行MySQL进程的用户必须具有写入所选目录的权限,否则命令将失败。
如果要从远程服务器(尤其是托管或虚拟机,如Heroku或Amazon RDS)将输出写入本地计算机,则此解决方案不适用。
迄今为止,除MySQL工作台之外的所有解决方案都是不正确的,并且对于mysql数据库中的至少一些可能内容而言可能是不安全的(即安全问题)。
MYSQL Workbench(以及类似的PHPMyAdmin)提供了一个正式正确的解决方案,但设计用于将输出下载到用户的位置。它们对于自动化数据导出等问题并不那么有用。
无法从mysql -B -e 'SELECT ...'
的输出生成可靠正确的csv,因为它无法对字段中的回车符和空格进行编码。 mysql的'-s'标志确实执行反斜杠转义,并可能导致正确的解决方案。但是,使用脚本语言(具有良好的内部数据结构,而不是bash),以及已经仔细制定编码问题的库会更安全。
我想为此写一个脚本,但是当我想到我称之为什么时,我就想到了用同一个名字搜索已有的作品。虽然我没有彻底解决它,但https://github.com/robmiller/mysql2csv的解决方案看起来很有希望。根据您的应用程序,指定SQL命令的yaml方法可能会或可能不会吸引人。我对于更新版本的ruby的要求并不像我的Ubuntu 12.04笔记本电脑或Debian Squeeze服务器那样标准。是的我知道我可以使用RVM,但我不想为了这么简单的目的而维护它。
希望有人会指出一个合适的工具,这需要进行一些测试。否则,当我找到或写一个时,我可能会更新它。
从命令行,您可以执行以下操作:
mysql -h *hostname* -P *port number* --database=*database_name* -u *username* -p -e *your SQL query* | sed 's/\t/","/g;s/^/"/;s/$/"/;s/\n//g' > *output_file_name.csv*
此页面上的许多答案都很薄弱,因为它们无法处理CSV格式的一般情况。例如字段中嵌入的逗号和引号以及最终总会出现的其他条件。我们需要一个适用于所有有效CSV输入数据的通用解决方案。
这是Python中一个简单而强大的解决方案:
#!/usr/bin/env python
import csv
import sys
tab_in = csv.reader(sys.stdin, dialect=csv.excel_tab)
comma_out = csv.writer(sys.stdout, dialect=csv.excel)
for row in tab_in:
comma_out.writerow(row)
将文件命名为tab2csv
,将其放在路径上,赋予它执行权限,然后像这样使用它:
mysql OTHER_OPTIONS --batch --execute='select * from whatever;' | tab2csv > outfile.csv
Python CSV处理功能涵盖了CSV输入格式的极端情况。
这可以通过流方法来改进以处理非常大的文件。
CREATE TABLE () (SELECT data FROM other_table ) ENGINE=CSV ;
创建CSV表时,服务器会在数据库目录中创建表格式文件。该文件以表名开头,扩展名为.frm。存储引擎还会创建一个数据文件。它的名称以表名开头,并具有.CSV扩展名。数据文件是纯文本文件。将数据存储到表中时,存储引擎会以逗号分隔值格式将其保存到数据文件中。
这很简单,它适用于任何不需要批处理模式或输出文件的东西:
select concat_ws(',',
concat('"', replace(field1, '"', '""'), '"'),
concat('"', replace(field2, '"', '""'), '"'),
concat('"', replace(field3, '"', '""'), '"'))
from your_table where etc;
说明:
"
替换""
- > replace(field1, '"', '""')
concat('"', result1, '"')
concat_ws(',', quoted1, quoted2, ...)
之间放置一个逗号而已!
这个答案使用Python和流行的第三方库PyMySQL。我正在添加它,因为Python的csv库足够强大,可以正确处理.csv
的许多不同风格,没有其他答案使用Python代码与数据库交互。
import contextlib
import csv
import datetime
import os
# https://github.com/PyMySQL/PyMySQL
import pymysql
SQL_QUERY = """
SELECT * FROM my_table WHERE my_attribute = 'my_attribute';
"""
# embedding passwords in code gets nasty when you use version control
# the environment is not much better, but this is an example
# https://stackoverflow.com/questions/12461484
SQL_USER = os.environ['SQL_USER']
SQL_PASS = os.environ['SQL_PASS']
connection = pymysql.connect(host='localhost',
user=SQL_USER,
password=SQL_PASS,
db='dbname')
with contextlib.closing(connection):
with connection.cursor() as cursor:
cursor.execute(SQL_QUERY)
# Hope you have enough memory :)
results = cursor.fetchall()
output_file = 'my_query-{}.csv'.format(datetime.datetime.today().strftime('%Y-%m-%d'))
with open(output_file, 'w', newline='') as csvfile:
# http://stackoverflow.com/a/17725590/2958070 about lineterminator
csv_writer = csv.writer(csvfile, lineterminator='\n')
csv_writer.writerows(results)
除上述答案外,您还可以拥有一个使用CSV引擎的MySQL表。
然后,您的硬盘上将有一个文件,该文件将始终采用CSV格式,您可以在不进行处理的情况下进行复制。
要扩展以前的答案,以下单行将单个表导出为制表符分隔文件。它适用于自动化,每天导出数据库。
mysql -B -D mydatabase -e 'select * from mytable'
方便的是,我们可以使用相同的技术列出MySQL的表,并描述单个表上的字段:
mysql -B -D mydatabase -e 'show tables'
mysql -B -D mydatabase -e 'desc users'
Field Type Null Key Default Extra
id int(11) NO PRI NULL auto_increment
email varchar(128) NO UNI NULL
lastName varchar(100) YES NULL
title varchar(128) YES UNI NULL
userName varchar(128) YES UNI NULL
firstName varchar(100) YES NULL
此外,如果您在Bash命令行上执行查询,我相信tr
命令可用于将默认选项卡替换为任意分隔符。
$ echo "SELECT * FROM Table123" | mysql Database456 | tr "\t" ,
在user7610的基础上,这是最好的方法。使用mysql outfile
,有60分钟的文件所有权和覆盖问题。
它并不酷,但它可以在5分钟内完成。
php csvdump.php localhost root password database tablename > whatever-you-like.csv
<?php
$server = $argv[1];
$user = $argv[2];
$password = $argv[3];
$db = $argv[4];
$table = $argv[5];
mysql_connect($server, $user, $password) or die(mysql_error());
mysql_select_db($db) or die(mysql_error());
// fetch the data
$rows = mysql_query('SELECT * FROM ' . $table);
$rows || die(mysql_error());
// create a file pointer connected to the output stream
$output = fopen('php://output', 'w');
// output the column headings
$fields = [];
for($i = 0; $i < mysql_num_fields($rows); $i++) {
$field_info = mysql_fetch_field($rows, $i);
$fields[] = $field_info->name;
}
fputcsv($output, $fields);
// loop over the rows, outputting them
while ($row = mysql_fetch_assoc($rows)) fputcsv($output, $row);
?>
$ mysql your_database --password=foo < my_requests.sql > out.csv
哪个是制表符分隔。像这样管道以获得真正的CSV(感谢@therefromhere):
... .sql | sed 's/\t/,/g' > out.csv
这是我做的:
echo $QUERY | \
mysql -B $MYSQL_OPTS | \
perl -F"\t" -lane 'print join ",", map {s/"/""/g; /^[\d.]+$/ ? $_ : qq("$_")} @F ' | \
mail -s 'report' person@address
perl脚本(从其他地方狙击)在将制表符间隔字段转换为CSV方面做得很好。
不完全是CSV格式,但MySQL客户端的tee
command可用于将输出保存到本地文件中:
tee foobar.txt
SELECT foo FROM bar;
你可以使用notee
禁用它。
SELECT … INTO OUTFILE …;
的问题是它需要在服务器上写文件的权限。
使用Tim发布的解决方案,我创建了这个bash脚本以方便该过程(请求root密码,但您可以轻松修改脚本以询问任何其他用户):
#!/bin/bash
if [ "$1" == "" ];then
echo "Usage: $0 DATABASE TABLE [MYSQL EXTRA COMMANDS]"
exit
fi
DBNAME=$1
TABLE=$2
FNAME=$1.$2.csv
MCOMM=$3
echo "MySQL password:"
stty -echo
read PASS
stty echo
mysql -uroot -p$PASS $MCOMM $DBNAME -B -e "SELECT * FROM $TABLE;" | sed "s/'/\'/;s/\t/\",\"/g;s/^/\"/;s/$/\"/;s/\n//g" > $FNAME
它将创建一个名为database.table.csv的文件
如果您在服务器上设置了PHP,则可以使用mysql2csv导出(实际上有效的)CSV文件以进行abitrary mysql查询。有关更多上下文/信息,请参阅my answer at MySQL - SELECT * INTO OUTFILE LOCAL ?。
我试图保持mysql
的选项名称,所以它应该足以提供--file
和--query
选项:
./mysql2csv --file="/tmp/result.csv" --query='SELECT 1 as foo, 2 as bar;' --user="username" --password="password"
通过“安装”mysql2csv
wget https://gist.githubusercontent.com/paslandau/37bf787eab1b84fc7ae679d1823cf401/raw/29a48bb0a43f6750858e1ddec054d3552f3cbc45/mysql2csv -O mysql2csv -q && (sha256sum mysql2csv | cmp <(echo "b109535b29733bd596ecc8608e008732e617e97906f119c66dd7cf6ab2865a65 mysql2csv") || (echo "ERROR comparing hash, Found:" ;sha256sum mysql2csv) ) && chmod +x mysql2csv
(下载要点的内容,检查校验和并使其可执行)。
什么对我有用:
SELECT *
FROM students
WHERE foo = 'bar'
LIMIT 0,1200000
INTO OUTFILE './students-1200000.csv'
FIELDS TERMINATED BY ',' ESCAPED BY '"'
ENCLOSED BY '"'
LINES TERMINATED BY '\r\n';
这个线程上没有任何解决方案适用于我的特定情况,我在其中一个列中有很多json数据,这会在我的csv输出中搞砸了。对于那些有类似问题的人,请尝试使用\ r \ n终止的行。
对于那些试图用Microsoft Excel打开csv的人来说,另一个问题是,请记住,单个单元格可以容纳32,767个字符的限制,高于它溢出到下面的行。要确定列中的哪些记录存在问题,请使用以下查询。然后,您可以截断这些记录或按照您的意愿处理它们。
SELECT id,name,CHAR_LENGTH(json_student_description) AS 'character length'
FROM students
WHERE CHAR_LENGTH(json_student_description)>32767;
用于对CSV转储进行简单查询的微小bash脚本,受https://stackoverflow.com/a/5395421/2841607的启发。
#!/bin/bash
# $1 = query to execute
# $2 = outfile
# $3 = mysql database name
# $4 = mysql username
if [ -z "$1" ]; then
echo "Query not given"
exit 1
fi
if [ -z "$2" ]; then
echo "Outfile not given"
exit 1
fi
MYSQL_DB=""
MYSQL_USER="root"
if [ ! -z "$3" ]; then
MYSQL_DB=$3
fi
if [ ! -z "$4" ]; then
MYSQL_USER=$4
fi
if [ -z "$MYSQL_DB" ]; then
echo "Database name not given"
exit 1
fi
if [ -z "$MYSQL_USER" ]; then
echo "Database user not given"
exit 1
fi
mysql -u $MYSQL_USER -p -D $MYSQL_DB -B -s -e "$1" | sed "s/'/\'/;s/\t/\",\"/g;s/^/\"/;s/$/\"/;s/\n//g" > $2
echo "Written to $2"
试试这段代码:
SELECT 'Column1', 'Column2', 'Column3', 'Column4', 'Column5'
UNION ALL
SELECT column1, column2,
column3 , column4, column5 FROM demo
INTO OUTFILE '/tmp/demo.csv'
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
LINES TERMINATED BY '\n';
有关更多信息:http://dev.mysql.com/doc/refman/5.1/en/select-into.html
以下bash脚本适合我。它还可以选择获取所请求表的模式。
#!/bin/bash
#
# export mysql data to CSV
#https://stackoverflow.com/questions/356578/how-to-output-mysql-query-results-in-csv-format
#
#ansi colors
#http://www.csc.uvic.ca/~sae/seng265/fall04/tips/s265s047-tips/bash-using-colors.html
blue='\033[0;34m'
red='\033[0;31m'
green='\033[0;32m' # '\e[1;32m' is too bright for white bg.
endColor='\033[0m'
#
# a colored message
# params:
# 1: l_color - the color of the message
# 2: l_msg - the message to display
#
color_msg() {
local l_color="$1"
local l_msg="$2"
echo -e "${l_color}$l_msg${endColor}"
}
#
# error
#
# show the given error message on stderr and exit
#
# params:
# 1: l_msg - the error message to display
#
error() {
local l_msg="$1"
# use ansi red for error
color_msg $red "Error:" 1>&2
color_msg $red "\t$l_msg" 1>&2
usage
}
#
# display usage
#
usage() {
echo "usage: $0 [-h|--help]" 1>&2
echo " -o | --output csvdirectory" 1>&2
echo " -d | --database database" 1>&2
echo " -t | --tables tables" 1>&2
echo " -p | --password password" 1>&2
echo " -u | --user user" 1>&2
echo " -hs | --host host" 1>&2
echo " -gs | --get-schema" 1>&2
echo "" 1>&2
echo " output: output csv directory to export mysql data into" 1>&2
echo "" 1>&2
echo " user: mysql user" 1>&2
echo " password: mysql password" 1>&2
echo "" 1>&2
echo " database: target database" 1>&2
echo " tables: tables to export" 1>&2
echo " host: host of target database" 1>&2
echo "" 1>&2
echo " -h|--help: show help" 1>&2
exit 1
}
#
# show help
#
help() {
echo "$0 Help" 1>&2
echo "===========" 1>&2
echo "$0 exports a csv file from a mysql database optionally limiting to a list of tables" 1>&2
echo " example: $0 --database=cms --user=scott --password=tiger --tables=person --output person.csv" 1>&2
echo "" 1>&2
usage
}
domysql() {
mysql --host $host -u$user --password=$password $database
}
getcolumns() {
local l_table="$1"
echo "describe $l_table" | domysql | cut -f1 | grep -v "Field" | grep -v "Warning" | paste -sd "," - 2>/dev/null
}
host="localhost"
mysqlfiles="/var/lib/mysql-files/"
# parse command line options
while true; do
#echo "option $1"
case "$1" in
# options without arguments
-h|--help) usage;;
-d|--database) database="$2" ; shift ;;
-t|--tables) tables="$2" ; shift ;;
-o|--output) csvoutput="$2" ; shift ;;
-u|--user) user="$2" ; shift ;;
-hs|--host) host="$2" ; shift ;;
-p|--password) password="$2" ; shift ;;
-gs|--get-schema) option="getschema";;
(--) shift; break;;
(-*) echo "$0: error - unrecognized option $1" 1>&2; usage;;
(*) break;;
esac
shift
done
# checks
if [ "$csvoutput" == "" ]
then
error "ouput csv directory not set"
fi
if [ "$database" == "" ]
then
error "mysql database not set"
fi
if [ "$user" == "" ]
then
error "mysql user not set"
fi
if [ "$password" == "" ]
then
error "mysql password not set"
fi
color_msg $blue "exporting tables of database $database"
if [ "$tables" = "" ]
then
tables=$(echo "show tables" | domysql)
fi
case $option in
getschema)
rm $csvoutput$database.schema
for table in $tables
do
color_msg $blue "getting schema for $table"
echo -n "$table:" >> $csvoutput$database.schema
getcolumns $table >> $csvoutput$database.schema
done
;;
*)
for table in $tables
do
color_msg $blue "exporting table $table"
cols=$(grep "$table:" $csvoutput$database.schema | cut -f2 -d:)
if [ "$cols" = "" ]
then
cols=$(getcolumns $table)
fi
ssh $host rm $mysqlfiles/$table.csv
cat <<EOF | mysql --host $host -u$user --password=$password $database
SELECT $cols FROM $table INTO OUTFILE '$mysqlfiles$table.csv'
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
LINES TERMINATED BY '\n';
EOF
scp $host:$mysqlfiles/$table.csv $csvoutput$table.csv.raw
(echo "$cols"; cat $csvoutput$table.csv.raw) > $csvoutput$table.csv
rm $csvoutput$table.csv.raw
done
;;
esac
如果您正在使用的计算机上安装了PHP,则可以编写PHP脚本来执行此操作。它要求PHP安装已安装MySQL扩展。
您可以从命令行调用PHP解释器,如下所示:
php --php-ini path/to/php.ini your-script.php
我包括--php-ini
开关,因为您可能需要使用自己的PHP配置来启用MySQL扩展。在PHP 5.3.0+上,默认情况下启用了该扩展,因此不再需要使用该配置来启用它。
然后你可以像任何普通的PHP脚本一样编写导出脚本:
<?php
#mysql_connect("localhost", "username", "password") or die(mysql_error());
mysql_select_db("mydb") or die(mysql_error());
$result = mysql_query("SELECT * FROM table_with_the_data p WHERE p.type = $typeiwant");
$result || die(mysql_error());
while($row = mysql_fetch_row($result)) {
$comma = false;
foreach ($row as $item) {
# Make it comma separated
if ($comma) {
echo ',';
} else {
$comma = true;
}
# Quote the quotes
$quoted = str_replace("\"", "\"\"", $item);
# Quote the string
echo "\"$quoted\"";
}
echo "\n";
}
?>
这种方法的优点是,varchar和text字段没有问题,它们的文本包含换行符。这些字段被正确引用,其中的这些新行将由CSV阅读器解释为文本的一部分,而不是记录分隔符。之后用sed左右很难纠正这个问题。
您可以在SQL编辑器/终端中使用以下命令:
“mysql -h(hostname / IP>) - u(用户名)-p(密码)databasename <(query.sql)> outputFILE(.txt / .xls)”
例如hostname -x.x.x.x.
uname - 用户名
密码 - 密码
DBName - employeeDB
queryFile - employee.sql
outputFile - outputFile.xls
mysql -hx.x.x.x -uusername -ppassword employeeDB <employee.sql> outputFile.xls
确保从SQL查询所在的目录执行命令,或者在上面的命令中提及sql查询位置的完整路径。
mysql --batch,-B
使用制表符作为列分隔符打印结果,每行都在新行上。使用此选项,mysql不使用历史记录文件。批处理模式导致非表格输出格式和特殊字符的转义。可以使用原始模式禁用转义;请参阅--raw选项的说明。
这将为您提供一个制表符分隔文件。由于逗号(或包含逗号的字符串)未进行转义,因此将分隔符更改为逗号并不简单。
这是一种相当粗糙的方式。发现它在某个地方,不能拿任何功劳
mysql --user=wibble --password wobble -B -e "select * from vehicle_categories;" | sed "s/'/\'/;s/\t/\",\"/g;s/^/\"/;s/$/\"/;s/\n//g" > vehicle_categories.csv
效果很好。再一次,虽然正则表达式证明只写。
正则表达式说明:
所以,把它们放在一起:
s/'/\'/ replace ' with \'
s/\t/\",\"/g replace all \t (tab) with ","
s/^/\"/ at the beginning of the line place a "
s/$/\"/ at the end of the line place a "
s/\n//g replace all \n (newline) with nothing
仅限Unix / Cygwin,通过'tr'管道:
mysql <database> -e "<query here>" | tr '\t' ',' > data.csv
N.B。:它既不处理嵌入式逗号,也不处理嵌入式选项卡。
这节省了我几次。快,它的工作原理!
--batch使用制表符作为列分隔符打印结果,每行都在新行上。
--raw禁用字符转义(\ n,\ t,\ 0和\)
例:
mysql -udemo_user -p -h127.0.0.1 --port=3306 \
--default-character-set=utf8mb4 --database=demo_database \
--batch --raw < /tmp/demo_sql_query.sql > /tmp/demo_csv_export.tsv
为了完整性你可以convert to csv(但要小心,因为标签可能在字段值内 - 例如文本字段)
tr '\t' ',' < file.tsv > file.csv
Paul Tomblin给出的OUTFILE解决方案会导致在MySQL服务器本身上写入文件,因此只有在您具有FILE访问权限以及登录访问权限或其他从该框中检索文件的方法时,此方法才有效。
如果您没有这样的访问权限,并且制表符分隔的输出是CSV的合理替代(例如,如果您的最终目标是导入到Excel),那么Serbaut的解决方案(使用mysql --batch
和可选的--raw
)就是可行的方法。
MySQL Workbench可以将记录集导出为CSV,它似乎可以很好地处理字段中的逗号。 CSV在OpenOffice中打开很好。
怎么样:
mysql your_database -p < my_requests.sql | awk '{print $1","$2}' > out.csv