我该如何以CSV格式输出MySQL查询结果?

1425

有没有一种简单的方式可以从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

当需要将许多列用引号括起来或结果中有需要转义的引号时,情况会变得混乱。


1
您可以在查询中使用 REPLACE() 函数来转义引号。 - dsm
看看我在这个stackoverflow上的回答。 - biniam
4
这个stackoverflow问题的被采纳的答案可能是最好的方式:https://dev59.com/RVDTa4cB1Zd3GeqPKp5i - Samuel Åslund
我在MariaDB bug跟踪器上编写了一个功能请求(https://jira.mariadb.org/browse/MDEV-12879),您可以对其投票。 - Jared Beck
https://dev59.com/aHNA5IYBdhLWcg3wBpHs - Sunil Rajput
显示剩余5条评论
41个回答

33

用法:

mysql your_database -p < my_requests.sql | awk '{print $1","$2}' > out.csv

3
我真的很喜欢这个。它更加干净,我喜欢使用awk。然而,我可能会选择这个:mysql -uUser -pPassword your_database < my_requests.sql | awk 'BEGIN{OFS="=";} {print $1,$2}' > out.csv - Josh
11
这对于含有空格的字段值无效。 - Barun Sharma

31

从命令行,您可以执行以下操作:

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*

来源: 从Amazon RDS导出表格到CSV文件


22
我遇到了同样的问题,但Paul's Answer不适用于Amazon RDS。 将制表符替换为逗号也无法解决问题,因为数据中包含嵌入的逗号和制表符。 我发现mycli是mysql客户端的即插即用替代品,并支持使用--csv标志导出CSV格式数据:

mycli db_name --csv -e "select * from flowers" > flowers.csv

非常好用,你可以通过以下方式在macOS上安装mycli:brew update && brew install mycli。我再也不会使用原生的mysql客户端了,mycli太棒了! - bithavoc
1
Mycli和它的表亲pgcli非常棒。我来这里添加这个mysql提示,因为我在其他解决方案上一无所获。使用mysql干净地输出到本地.csv文件是令人惊讶的困难。 - iturgeon
太棒了,很遗憾这个回答只得到了极少数的“赞”。它是唯一符合我的需求的解决方案。其他所有方案对我来说都不完美,但是这个就很好。我在列中添加了换行符,这导致在打开文件时,在 Excel 程序中出现了新行。 - Skiff
如果你被困在使用1.8.1版本的情况下(可能是因为你正在使用仍然拥有Python 2.x的旧操作系统),那么这个答案将无法工作,因为'--csv'在那个版本中不可用。 - Joseph Van Riper
我对其他解决方案为什么会得到更多的赞感到困惑。谢谢您分享关于mycli的信息,现在它是我的默认客户端了。 - RobMac

21

这个答案使用了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://dev59.com/RGcs5IYBdhLWcg3w8obK
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:
    # https://dev59.com/rnA75IYBdhLWcg3wo6le#17725590 about lineterminator
    csv_writer = csv.writer(csvfile, lineterminator='\n')
    csv_writer.writerows(results)

已经提供了一个使用Python的答案。https://dev59.com/tHRC5IYBdhLWcg3wROtQ#35123787 - Alexander Baltasar
5
@AlexanderBaltasar,没错,看起来很有用,但它没有使用Python代码与数据库进行交互。请查看该问题的评论。 - Ben
“没有其他答案使用Python代码与数据库交互。”的意思是:不,但是Chris Johnson的答案也使用了'csv'库。 - Peter Mortensen
@PeterMortensen,那个回答的评论激励我写下了这个答案 :) - Ben

14
此外,如果您正在Bash命令行上执行查询,我相信可以使用tr命令将默认制表符替换为任意分隔符。
$ echo "SELECT * FROM Table123" | mysql Database456 | tr "\t" ,

如果您在文件权限方面遇到麻烦,这是一个不错的选择,但它不会引用您的字段,因此如果您的数据包含逗号或引号,其他工具可能会错误解释 CSV。 - Paul A Jungwirth
需要使用 -s(静默)选项才能生成带制表符的输出。 - JamesP

14

这很简单,并且适用于任何不需要批处理模式或输出文件的内容:

select concat_ws(',',
    concat('"', replace(field1, '"', '""'), '"'),
    concat('"', replace(field2, '"', '""'), '"'),
    concat('"', replace(field3, '"', '""'), '"'))

from your_table where etc;

解释:

  1. 在每个字段中用 "" 替换 " --> replace(field1, '"', '""')
  2. 将每个结果用引号括起来 --> concat('"', result1, '"')
  3. 在每个带引号的结果之间加上逗号 --> concat_ws(',', quoted1, quoted2, ...)

就是这样!


4
请注意,concat_ws() 函数将跳过 NULL 值,导致列不匹配。为了避免这种情况,可以使用空字符串代替:IFNULL(field, '')(或者使用其他代表 NULL 的值)。 - Marco Roy

11

您可以创建一个使用CSV引擎的MySQL数据表。

然后,您将拥有一个始终以CSV格式存在于硬盘上的文件,可以直接复制而无需进行处理。


3
在文件大小、写入、读取等方面,这个有什么限制? - Zach Smith

10

为了进一步解释之前的答案,下面这个一行代码可以将单个表格导出为一个以Tab分隔的文件。它适用于自动化,例如每天导出数据库。

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    

4
将数据转换为CSV格式:mysql -B -D mydatabase -e 'select * from mytable' | sed -e 's/\t/,/g' - DSimon
8
如果你确定数据中没有逗号或制表符,使用 sed -e 's/\t/,/g' 是安全的。该命令的作用是将制表符替换为逗号。 - awatts
@awatts 我建议使用:sed -e 's/"/\\"/g' -e 's/\t/","/g' -e 's/^/"/' -e 's/$/"/' - Flimm

8

在用户7610的基础上,这是最好的方法。 使用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);

?>

1
好的,干净的,快速运行 - 如果您可以在该环境中运行PHP,则这是一个很好的解决方案! - John Fiala

8

这是我的工作内容:

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。

2
这很棒。稍微改进一下,可以引用除数字外的所有内容 perl -F"\t" -lane 'print join ",", map {s/"/""/g; /^\d+(?:\.\d+)?$/ ? $_ : qq("$_")} @F ' -- 你的代码没有引用 1.2.3 - artfulrobot
1
在我看来,这应该是被接受的解决方案,当然还有@artfulrobot的改进。 - chrisinmtown

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接