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

How can I output MySQL query results in CSV format?

提问人:MCS 提问时间:12/10/2008 最后编辑:Peter MortensenMCS 更新时间:9/30/2023 访问量:1599105

问:

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

当有很多列需要用引号括起来时,或者如果结果中有引号需要转义,它就会变得混乱。

MySQL CSV 报价

评论

1赞 dsm 12/11/2008
您可以在查询中使用 REPLACE() 来对引号进行转义。
0赞 biniam 4/2/2015
看看我在 [this stackoverflow][1] [1] 中的回答:stackoverflow.com/questions/12242772/......
0赞 Somnath Muluk 12/4/2015
您可以将表数据格式化为文本表
4赞 Samuel Åslund 1/23/2016
这个 stackoverflow 问题的公认答案可能是最好的方法:stackoverflow.com/questions/3760631/mysql-delimiter-question
0赞 Jared Beck 5/24/2017
我在 MariaDB 错误跟踪器上写了一个功能请求(jira.mariadb.org/browse/MDEV-12879)你可以对它进行投票。

答:

2008赞 Paul Tomblin 12/11/2008 #1

“将 MySQL 查询结果保存到文本或 CSV 文件中”中:

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';

注意:该语法可能需要重新排序为

SELECT order_id,product_name,qty
INTO OUTFILE '/var/lib/mysql-files/orders.csv'
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
LINES TERMINATED BY '\n'
FROM orders
WHERE foo = 'bar';

在MySQL的最新版本中。

使用此命令,将不会导出列名。

另请注意,它将位于运行 MySQL 的服务器上。运行 MySQL 进程的用户必须有权写入所选目录,否则命令将失败。/var/lib/mysql-files/orders.csv

如果要将输出从远程服务器(尤其是托管或虚拟化计算机,如 HerokuAmazon RDS)写入本地计算机,则此解决方案不适用。

评论

10赞 Curtis Yallop 6/24/2021
您可能需要成为 root 用户。“文件是在服务器主机上创建的,因此您必须具有 FILE 权限才能使用此语法 dev.mysql.com/doc/refman/8.0/en/select-into.html。”
0赞 cazort 7/4/2021
是的,它既依赖于MySQL权限,也依赖于对本地文件系统上某处的写入权限。
0赞 mojuba 9/25/2021
LINES TERMINATED BY '\n'不是必需的,这是默认设置。通常,如果省略所有其他参数,则会得到一个制表符分隔的文件,其中没有包含字段。
1赞 Chris 1/19/2022
关于保存文件的路径,这与 secure-priv-file 指令有关。如果您不想(或不能)使用 root,您也可以在 my.ini 中禁用它并重新启动数据库或更改该目录的权限。请参阅 stackoverflow.com/questions/32737478/...
1赞 Carl 3/24/2023
对于 RDS,如果您使用的是 Aurora mysql,则可以选择 INTO OUTFILE S3 docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/...
11赞 Jonathan 12/11/2008 #2

您可以有一个使用 CSV 引擎的 MySQL 表。

然后,您的硬盘上将有一个文件,该文件将始终采用CSV格式,您可以复制该文件而不对其进行处理。

评论

3赞 Zach Smith 10/10/2018
在文件大小/写入/读取/等方面有什么限制?
241赞 serbaut 9/30/2009 #3

mysql --batch, -B

使用 tab 作为列分隔符打印结果,每行位于 换行。使用此选项,mysql 不使用历史记录文件。 批处理模式会导致非表格输出格式和转义 特殊字符。可以使用原始模式禁用转义;看 --raw 选项的描述。

这将为您提供一个制表符分隔的文件。由于逗号(或包含逗号的字符串)不会转义,因此将分隔符更改为逗号并不简单。

566赞 Stan 4/9/2010 #4
mysql your_database --password=foo < my_requests.sql > out.tsv

这将生成制表符分隔的格式。如果您确定逗号不会出现在任何列数据中(制表符也不会出现),则可以使用此管道命令获取真正的 CSV(感谢用户 John Carter):

... .sql | sed 's/\t/,/g' > out.csv

评论

135赞 Joey T 12/11/2012
SED“修复”不会补偿任何选定数据中可能出现的逗号,并且会相应地扭曲输出的列
0赞 Rodrigo 2/16/2023
每个人都说在命令行上拼写密码是不安全的。
45赞 Leland Woodbury 6/22/2010 #5

Paul Tomblin 给出的 OUTFILE 解决方案会导致在 MySQL 服务器本身上写入文件,因此仅当您具有 FILE 访问权限以及登录访问权限或其他从该框中检索文件的方法时,这才有效。

如果您没有这样的访问权限,并且制表符分隔的输出是 CSV 的合理替代品(例如,如果您的最终目标是导入到 Excel),那么 serbaut 的解决方案(使用 和可选 )是要走的路。mysql --batch--raw

203赞 Tim Harding 3/23/2011 #6

这里有一个相当粗糙的方法[1]

mysql --user=wibble --password mydatabasename -B -e "select * from vehicle_categories;" | sed "s/'/\'/;s/\t/\",\"/g;s/^/\"/;s/$/\"/;s/\n//g" > vehicle_categories.csv

它工作得很好。不过,正则表达式再次被证明是只写的。


正则表达式说明:

  • s/// 表示用第二个之间的内容替换 //
  • 末尾的“g”是一个修饰语,表示“所有实例,而不仅仅是第一个实例”
  • ^ (在此上下文中)表示行的开头
  • $(在此上下文中)表示行尾

所以,把它们放在一起:

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

[1] 我在某个地方找到了它,不能相信任何功劳。

评论

0赞 avernus 7/1/2022
命令中有拼写错误:“--password=wobble”
2赞 Tim Harding 7/7/2022
@AydinÖzcan只是在执行命令后提示用户输入密码。 是数据库名称,但我同意这不清楚,为了清楚起见,我对其进行了编辑。谢谢。--passwordwobble
8赞 extraplanetary 7/8/2011 #7

这是我的工作:

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赞 artfulrobot 3/16/2017
这太棒了。稍作改进可能是引用除数字之外的所有内容 - 您的不会引用perl -F"\t" -lane 'print join ",", map {s/"/""/g; /^\d+(?:\.\d+)?$/ ? $_ : qq("$_")} @F '1.2.3
1赞 chrisinmtown 9/25/2020
恕我直言,这应该是公认的解决方案,当然还有@artfulrobot的改进。
33赞 Steve 11/11/2011 #8

用:

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

评论

3赞 Josh 4/11/2012
我真的很喜欢这个。它更干净,我喜欢使用 awk。但是,我可能会这样做:mysql -uUser -pPassword your_database < my_requests.sql | awk 'BEGIN{OFS="=";} {print $1,$2}' > out.csv
11赞 Barun Sharma 4/10/2015
对于带有空格的字段值,此操作失败。
14赞 Marty Hirsch 12/7/2011 #9

这很简单,它可以在不需要批处理模式或输出文件的情况下处理任何事情:

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赞 Marco Roy 5/22/2019
请注意,值将被跳过,从而导致列不匹配。为了避免这种情况,只需使用空字符串:(或你用来表示的任何其他内容NULLconcat_ws()IFNULL(field, '')NULL)
0赞 3 revs, 2 users 54%user7610 #10

如果您使用的机器上安装了 PHP,您可以编写一个 PHP 脚本来执行此操作。它要求PHP安装已安装MySQL扩展。

您可以从命令行调用 PHP 解释器,如下所示:

php --php-ini path/to/php.ini your-script.php

我包括开关,因为您可能需要使用自己的PHP配置来启用MySQL扩展。在 PHP 5.3.0+ 上,该扩展默认启用,因此不再需要使用配置来启用它。--php-ini

然后,您可以像编写任何普通的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 和文本字段没有问题,这些字段的文本包含换行符。这些字段被正确引用,CSV 阅读器将解释其中的换行符作为文本的一部分,而不是记录分隔符。这是事后很难用 sed 左右纠正的事情。

评论

1赞 mc0e 10/10/2013
这远非不必要的复杂,而是这里唯一朝着可能正确的方向发展的解决方案 - 尽管需要更多的工作。CSV 比最初看起来更复杂,而且您犯了各种错误。例如,原文中的反斜杠。最好使用一个已经解决了写入 CSV 的所有问题的库。
0赞 user7610 11/2/2015
@mc0e 通常,您要么将报价加倍,要么转义报价。我决定将报价加倍,因此我不需要转义字符。不同的软件对CSV细节有不同的想法。例如,MySQL 中的 LOAD DATA 确实将 \ 视为转义字符,而 Open Office Calc 则不然。当我写下答案时,我正在将数据导出到电子表格中。
1赞 user7610 10/22/2018
我的代码处理了当天导出数据集所需的一切;)您的答案也是朝着正确方向迈出的一步,但正如第一条评论所说,它应该 1) 直接从脚本连接到 sql 数据库,以及 2) 使用适当的 csv 库。
42赞 David Oliver 6/27/2012 #11

MySQL Workbench 可以将记录集导出为 CSV,并且它似乎可以很好地处理字段中的逗号。CSV 在 OpenOffice Calc 中打开正常。

评论

3赞 mr-euro 7/12/2012
谢谢一百万大卫。在花了 3 个小时让换行符正确输出数据中的 HTML 内容后,我使用了 MySQL Workbench,并在 2 分钟内准备好了我的 CSV 文件。
0赞 David Oliver 8/6/2012
我刚刚发现它也可以另存为 XML,这很棒。我希望通过使用 XSLT 将此 XML 转换为适合导入目标应用程序的 CSV 文件,从一个应用程序迁移到另一个应用程序。
0赞 cijagani 8/5/2015
MySQL Workbench 是导入导出功能的最佳选择。
0赞 LancelotHolmes 10/29/2016
好吧,但是每次工作台将选择记录限制为1000条,而当涉及到更多记录时,它就不能很好地工作了,如果我尝试将一个相对较大的csv文件导入到mysql数据库中,它经常被阻止。
1赞 Vincent 4/22/2018
我刚刚使用 mysql workbench 成功导出了超过五十万行,因此大文件似乎不是问题。您只需要确保在运行查询之前删除选择限制,并且可能还必须增加 my.ini 文件中的以下值:max_allowed_packet = 500M、net_read_timeout = 600、net_write_timeout = 600
132赞 strickli 10/11/2012 #12

通过“tr”管道传输它(仅限 Unix/Cygwin):

mysql <database> -e "<query here>" | tr '\t' ',' > data.csv

注意:这既不处理嵌入的逗号,也不处理嵌入的制表符。

评论

5赞 cazort 7/4/2021
我对这个解决方案的赞成率如此之低感到惊讶。目前,顶级解决方案需要许多数据库用户所没有的权限(这是有充分理由的;管理员提供它是一种安全风险。您的解决方案无需任何特殊权限即可工作,并且还可以改进以解决逗号或制表符的缺点,可能在查询本身中进行替换。
1赞 gordon_freeman 8/8/2022
同意这里的评论,这正是避免mysql特权地狱所需要的。
4赞 lepe 5/16/2013 #13

使用 Tim Harding 发布的解决方案,我创建了这个 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 的文件

评论

0赞 chrisinmtown 9/25/2020
嵌入的双引号字符呢?他们也需要逃脱,但我在 sed 咒语中没有看到他们的模式。
37赞 mc0e 10/11/2013 #14

到目前为止,除了MySQL Workbench解决方案之外,这里的所有解决方案都是不正确的,并且对于MySQL数据库中至少某些可能的内容来说很可能是不安全的(即安全问题)。

MySQL Workbench(以及类似的phpMyAdmin)提供了一个形式上正确的解决方案,但它们旨在将输出下载到用户的位置。它们对于自动导出数据等事情不是那么有用。

无法从 的输出中生成可靠正确的 CSV 内容,因为无法对字段中的回车符和空格进行编码。“-s”标志确实会进行反斜杠转义,并可能导致正确的解决方案。但是,使用脚本语言(具有良好的内部数据结构,而不是 Bash)和已经仔细解决编码问题的库要安全得多。mysql -B -e 'SELECT ...'mysql

我曾想过为此写一个剧本,但当我想到我该怎么称呼它时,我就想到要搜索同名的现有作品。虽然我还没有彻底了解它,但 mysql2csv 看起来很有希望。不过,根据您的应用程序,指定 SQL 命令的 YAML 方法可能会吸引人,也可能不会吸引人。我对 Ubuntu 12.04 (Precise Pangolin) 笔记本电脑或 Debian 6.0 (Squeeze) 服务器标配的 Ruby 版本的要求也并不感到兴奋。是的,我知道我可以使用 RVM,但我宁愿不为了这么简单的目的而维护它。

评论

1赞 yeya 7/28/2016
你是对的,对于表中的复杂字符串,你必须使用一些像样的库,而不仅仅是 bash。我认为nodejs对这种行为有更好的解决方案。像这个例子
1赞 reallynice 6/2/2017
嗨,很好的答案,我会添加一个指向下面 stackoverflow.com/a/35123787/1504300 建议的 python 解决方案的链接,该解决方案运行良好且非常简单。我试图编辑你的帖子,但编辑被拒绝了
0赞 chrisinmtown 9/10/2020
Rob Miller 的 mysql2csv 脚本坚持通过网络或套接字连接到数据库本身,不能用作 unix 风格的管道。也许这是必需的,但它确实限制了使用。
0赞 mc0e 10/4/2020
@chrisinmtown你想在里面注入什么?也许是 myslqldump 的输出?正如我所指出的,输出是无法修复的。mysql -B
1赞 chrisinmtown 10/5/2020
@mc0e我想通过管道传入mysqldump的输出,请将其读取为stdin。
7赞 Denilson Sá Maia 5/30/2014 #15

不完全是CSV格式,但MySQL客户端tee命令可用于将输出保存到本地文件中:

tee foobar.txt
SELECT foo FROM bar;

您可以使用 禁用它。notee

问题在于它需要权限才能在服务器上写入文件。SELECT … INTO OUTFILE …;

评论

0赞 datalifenyc 5/9/2018
如果使用 .csv 扩展名而不是 .txt,是否有任何格式问题需要注意?
0赞 jkmartindale 6/21/2019
@myidealab 格式问题是由逗号等未转义引起的。CSV 是一种纯文本格式,因此仅换出扩展名就不会出现格式问题。
-1赞 Indrajeet Singh 9/8/2014 #16

试试这个代码:

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

评论

0赞 zus 2/19/2020
如果我运行以下查询,我收到错误,则考虑相同。我的查询:justpaste.it/2fp6f 任何帮助谢谢。@Indrajeet辛格
1赞 Peter Mortensen 8/6/2021
解释是有序的。例如,想法/要点是什么?请通过编辑(更改)您的答案来回复,而不是在评论中(没有“编辑:”,“更新:”或类似内容 - 答案应该看起来像今天写的一样)。
10赞 johntellsall 10/24/2014 #17

为了扩展前面的答案,以下单行代码将单个表导出为制表符分隔的文件。它适用于自动化,每天左右导出数据库。

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赞 DSimon 1/13/2015
要转换为 CSV:mysql -B -D mydatabase -e 'select * from mytable' | sed -e 's/\t/,/g'
8赞 awatts 10/21/2015
只有在确定数据不包含任何逗号或制表符的情况下,使用才是安全的。sed -e 's/\t/,/g'
0赞 Flimm 2/15/2023
@awatts我建议:sed -e 's/"/\\"/g' -e 's/\t/","/g' -e 's/^/"/' -e 's/$/"/'
8赞 Michael Cole 5/30/2015 #18

在 user7610 的基础上构建,这是最好的方法。有 60 分钟的文件所有权和覆盖问题。mysql outfile

这并不酷,但它在 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赞 John Fiala 4/6/2018
漂亮、干净、快速工作 - 如果你能在那种环境中运行 PHP,这是一个很好的解决方案!
31赞 Sri Murthy Upadhyayula 6/25/2015 #19

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

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 文件

83赞 hrvoj3e 1/29/2016 #20

这救了我几次。它速度快,而且有效!

--batch 使用 tab 作为列分隔符打印结果,每行位于 换行。

--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

为了完整起见,您可以转换为 CSV(但要小心,因为选项卡可能位于字段值内 - 例如,文本字段)

tr '\t' ',' < file.tsv > file.csv

评论

0赞 Hashim Aziz 11/22/2023
这是一个完全不同的问题的答案。
34赞 Chris Johnson 2/1/2016 #21

此页面上的许多答案都很弱,因为它们无法处理 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 输入格式的极端情况。

这可以改进,以通过流式处理方法处理非常大的文件。

评论

11赞 Josh Rumbut 2/4/2016
一个更可靠的解决方案是使用 Python 实际连接到数据库,然后您应该可以更轻松地完成处理更大数据集(分块结果、流式处理等)所需的操作。
0赞 Ben 8/31/2019
@JoshRumbut,真的晚了,但我做了 stackoverflow.com/a/41840534/2958070 来补充你的评论
0赞 chrisinmtown 9/10/2020
请参阅 stackoverflow.com/questions/356578/...,了解此脚本的扩展版本,其中包含处理嵌入逗号和双引号字符的输入方言!
1赞 Peter Mortensen 8/6/2021
它的要点是什么?使用库?内置库?你能链接到文档等吗?(但是如果没有“编辑:”,“更新:”或类似内容 - 答案应该看起来像是今天写的一样。
1赞 Peter Mortensen 8/12/2021
流式处理方法需要什么?你能在你的回答中详细说明一下吗?(但是如果没有“编辑:”,“更新:”或类似内容,答案应该看起来像今天写的一样。
21赞 Ben 1/25/2017 #22

这个答案使用 Python 和流行的第三方库 PyMySQL。我添加它是因为 Python 的 csv 库足够强大,可以正确处理许多不同风格的,并且没有其他答案使用 Python 代码与数据库进行交互。.csv

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)

评论

0赞 Alexander Baltasar 1/25/2017
已经提供了使用 python 的答案。stackoverflow.com/a/35123787/5470883
5赞 Ben 1/26/2017
@AlexanderBaltasar,对,它看起来很有用,但它没有使用 Python 代码与数据库交互。请参阅关于该问题的评论。
0赞 Peter Mortensen 8/6/2021
Re “no other answers are using Python code to interact with the database.”: 不,但 Chris Johnson 的答案也使用了 'csv' 库。
0赞 Ben 8/7/2021
@PeterMortensen,对这个答案的评论促使我写下这个答案:)
14赞 user2427354 1/13/2018 #23

此外,如果您在 Bash 命令行上执行查询,我相信该命令可用于将默认制表符替换为任意分隔符。tr

$ echo "SELECT * FROM Table123" | mysql Database456 | tr "\t" ,

评论

0赞 Paul A Jungwirth 4/8/2021
如果您在文件权限方面遇到问题,这是一个很好的方法,但它不会引用您的字段,因此,如果您的数据包含逗号或引号,其他工具可能会误解 CSV。
0赞 JamesP 7/19/2021
需要 -s(静默)选项来生成选项卡式输出
2赞 minitauros 3/1/2018 #24

用于对 CSV 转储进行简单查询的微型 Bash 脚本,灵感来自 Tim Harding 的答案

#!/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"

评论

1赞 chrisinmtown 9/25/2020
这使用忽略嵌入的双引号字符的 sed 咒语。建议改用 perl 解决方案。
1赞 Wolfgang Fahl 5/14/2018 #25

以下 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 standard error and exit
#
#   Parameters:
#     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 "output CSV directory is not set"
fi
if [ "$database" == "" ]
then
  error "MySQL database is not set"
fi
if [ "$user" == "" ]
then
  error "MySQL user is not set"
fi
if [ "$password" == "" ]
then
  error "MySQL password is 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
3赞 Hirnhamster 5/16/2018 #26

如果您在服务器上设置了PHP,则可以使用mysql2csv为任意MySQL查询导出(实际上有效的)CSV文件。请参阅我在 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

(下载要点的内容,检查校验和并使其可执行。

5赞 Rohit Chemburkar 11/7/2018 #27

什么对我有用:

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;

评论

0赞 Peter Mortensen 8/6/2021
回复“......漂亮的 JSON 数据...“:漂亮在什么方面?
3赞 humbads 7/17/2019 #28

下面生成制表符分隔的有效 CSV 输出。与大多数其他答案不同,此技术可以正确处理制表符、逗号、引号和换行符的转义,而无需任何流过滤器,如 sedAWKtr

该示例展示了如何使用流将远程 MySQL 表直接通过管道传递到本地 SQLite 数据库。这无需 FILE 权限或 SELECT INTO OUTFILE 权限即可工作。为了提高可读性,我添加了新行。

mysql -B -C --raw -u 'username' --password='password' --host='hostname' 'databasename'
-e 'SELECT
    CONCAT('\''"'\'',REPLACE(`id`,'\''"'\'', '\''""'\''),'\''"'\'') AS '\''id'\'',
    CONCAT('\''"'\'',REPLACE(`value`,'\''"'\'', '\''""'\''),'\''"'\'') AS '\''value'\''
    FROM sampledata'
2>/dev/null | sqlite3 -csv -separator $'\t' mydb.db '.import /dev/stdin mycsvtable'

需要在命令行上禁止显示有关密码的警告。2>/dev/null

如果数据具有 NULL,则可以在查询中使用 IFNULL() 函数。

-3赞 saran3h 11/6/2019 #29

如果你在生产或任何其他服务器上,无法访问文件系统,你可以使用这个简单的技巧和一点点的手动工作来获得你想要的东西。

步骤 1。只需将所有列包装在下方并使用MySQL提供的选项即可获得逗号分隔的结果(或使用所需的任何分隔符)。下面是一个示例:CONCATas CSVFormat

SELECT
    CONCAT(u.id,
            ',',
            given,
            ',',
            family,
            ',',
            email,
            ',',
            phone,
            ',',
            ua.street_number,
            ',',
            ua.route,
            ',',
            ua.locality,
            ',',
            ua.state,
            ',',
            ua.country,
            ',',
            ua.latitude,
            ',',
            ua.longitude) AS CSVFormat
FROM
    table1 u
        LEFT JOIN
    table2 ua ON u.address_id = ua.id
WHERE
    role_policy = 31 and is_active = 1;

第2步。将结果从终端复制到文件中,并使用任何文本编辑器清理所有竖线字符(构成结果布局)。

第 3 步。另存为 .csv 文件,仅此而已。

2赞 AAYUSH SHAH 12/20/2019 #30

如果您收到错误,那么在将目标文件位置移入其中之后以及查询之后 -secure-file-privC:\ProgramData\MySQL\MySQL Server 8.0\Uploads

SELECT * FROM attendance INTO OUTFILE 'C:\ProgramData\MySQL\MySQL Server 8.0\Uploads\FileName.csv' FIELDS TERMINATED BY ',' ENCLOSED BY '"' LINES TERMINATED BY '\n';

不起作用,您只需将 (backsplash) 从查询更改为 (forwardsplash)\/

这很有效!

例:

选择 * 从考勤到输出文件“C:/ProgramData/MySQL/MySQL Server 8.0/Uploads/FileName.CSV”字段以“,”结尾,用“”“结尾的行以”\n“结尾;

每次运行成功的查询时,它都会生成新的 CSV 文件! 很酷,对吧?

评论

0赞 Peter Mortensen 8/6/2021
大概在 Windows 上?它测试了什么,包括版本(Windows、MySQL等)?
0赞 Alex Ryan 2/3/2020 #31

此解决方案将 SQL 查询放在 heredoc 中,并通过筛选器传递输出:

文件 query.sh

#!/bin/bash

mysql --defaults-group-suffix=[DATABASE_NAME] --batch << EOF | python query.py
SELECT [FIELDS]
FROM [TABLE]
EOF

此版本的 Python 过滤器无需使用模块即可工作:csv

文件 query.sh

import sys

for line in sys.stdin:
    print(','.join(["\"" + str(element) + "\"" for element in line.rstrip('\n').split('\t')]))

此版本的 Python 过滤器使用 CSV 模块,涉及的代码略多,但可以说更清晰一些:

文件 query.sh

import csv, sys

csv_reader = csv.reader(sys.stdin, delimiter='\t')
csv_writer = csv.writer(sys.stdout, quoting=csv.QUOTE_NONNUMERIC)

for line in csv_reader:
    csv_writer.writerow(line)

或者你可以使用 Pandas

文件 query.py

import csv, sys
import pandas as pd

df = pd.read_csv(sys.stdin, sep='\t')
df.to_csv(sys.stdout, index=False, quoting=csv.QUOTE_NONNUMERIC)

评论

0赞 Peter Mortensen 8/6/2021
不就是第一个名为“query.sh”的文件吗?
22赞 Shenal Silva 2/16/2020 #32

我遇到了同样的问题,Paul's Answer 不是一个选项,因为它是 Amazon RDS。用逗号替换制表符不起作用,因为数据嵌入了逗号和制表符。我发现 mycli 是 mysql-client 的直接替代品,它支持带有标志的开箱即用的 CSV 输出:--csv

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

评论

0赞 bithavoc 7/24/2020
就像一个魅力,您可以通过以下方式在macOS中安装mycli: .我再也不使用股票 mysql 客户端了,mycli 太合法了!brew update && brew install mycli
1赞 iturgeon 9/5/2020
Mycli 和它的表亲 pgcli 太棒了。我来这里是为了添加这个技巧 mysql,因为我没有使用其他解决方案。使用 mysql 干净地输出到本地 .csv 非常困难。
0赞 Skiff 11/21/2020
太棒了,太可悲了,这个答案得到的“喜欢”很少。这是唯一符合我需求的解决方案。所有其他对我来说都不是完美的,但这个。我在列中有换行符 - 这导致打开文件时 Excel 程序中出现新行。
0赞 Joseph Van Riper 12/14/2020
如果您坚持使用 1.8.1 版本(可能是因为您使用的是仍然具有 Python 2.x 的旧操作系统),则此答案将不起作用,因为该版本中不提供“--csv”。
0赞 RobMac 4/30/2023
我很困惑为什么其他解决方案不断得到支持。感谢您分享有关 mycli 的信息,它现在是我的默认客户端。
-1赞 Sudarshan 3/25/2020 #33

对于那些可能想要下载 CSV 格式的查询结果,但无权访问服务器文件但无法访问数据库的人。

首先,它不是 Linux 命令。步骤如下:

  1. 使用查询创建视图。例如:(Create VIEW v as (Select * from user where status = 0))
  2. 该视图将在数据库的某个部分下创建。view
  3. 现在将视图导出为 .CSV
  4. 如果需要表列作为 CSV 的标题,请设置为并选中 。Export method:Custom - display all possible optionsPut columns names in the first row
0赞 Pranav 4/4/2020 #34

如果您在尝试导出文件时遇到此错误

错误 1290 (HY000):MySQL 服务器正在运行 --secure-file-priv 选项,使其无法执行此语句

并且您无法解决此错误,只需运行此 Python 脚本即可完成一件事

import mysql.connector
import csv

con = mysql.connector.connect(
    host="localhost",
    user="root",
    passwd="Your Password"
)

cur = con.cursor()

cur.execute("USE DbName")
cur.execute("""
select col1,col2 from table
where <cond>
""")

with open('Filename.csv',mode='w') as data:
    fieldnames=["Field1","Field2"]
    writer=csv.DictWriter(data,fieldnames=fieldnames)
    writer.writeheader()
    for i in cur:
        writer.writerow({'Field1':i[0],'Field2':i[1]})

评论

0赞 stephenwade 4/5/2020
欢迎来到 Stack Overflow!这并不能回答这个问题。请查看 stackoverflow.com/help/how-to-answer
1赞 chrisinmtown 9/10/2020 #35

站在克里斯·约翰逊(Chris Johnson)的肩膀上,我从2016年2月开始用自定义方言扩展了答案。

这个 shell 管道工具不需要连接到您的数据库,处理随机逗号和引号 输入,并且在 Python 2 Python 3 中运行良好!

#!/usr/bin/env python
import csv
import sys

# fields are separated by tabs; double-quotes may occur anywhere
csv.register_dialect("mysql", delimiter="\t", quoting=csv.QUOTE_NONE)
tab_in = csv.reader(sys.stdin, dialect="mysql")
comma_out = csv.writer(sys.stdout, dialect=csv.excel)
for row in tab_in:
    # print("row: {}".format(row))
    comma_out.writerow(row)

使用该 print 语句说服自己它正在正确解析您的输入:)

一个主要的警告:回车符的处理,^M aka control-M,在 Linux 术语中是 \r。尽管批处理模式MySQL输出正确地转义了嵌入的换行符,因此每行确实有一行(由Linux换行符\n定义),但MySQL在列数据周围没有引号。如果数据项具有嵌入的回车符,则 csv.reader 会拒绝该输入,但会出现以下异常:

new-line character seen in unquoted field -
do you need to open the file in universal-newline mode?

请不要@我说我应该使用通用文件模式,以模式“rU”重新打开sys.stdin.fileno。我试过了,它会导致嵌入的 \r 字符被视为记录结束标记,因此单个输入记录被错误地转换为许多不完整的输出记录。

我还没有找到针对 Python 的 csv.reader 模块的此限制的 Python 解决方案。我认为根本原因是他们的文档csv.reader中指出的csv.reader实现/限制:

The reader is hard-coded to recognise either '\r' or '\n' as end-of-line,
and ignores lineterminator.

我能提供的弱且不令人满意的解决方案是在 Python 的 csv.reader 看到数据之前将每个 \r 字符更改为两个字符序列“\n”。我使用了 sed 命令。下面是一个带有 MySQL select 和上面的 Python 脚本的管道示例:

mysql -u user db --execute="select * from table where id=12345" \
  | sed -e 's/\r/\\n/g' \
  | mysqlTsvToCsv.py

在与这个问题斗争了一段时间之后,我认为 Python 不是正确的解决方案。如果你能接受Perl,我认为artfulrobot提供的单行脚本可能是最有效和最简单的解决方案。

6赞 Md. Robi Ullah 9/21/2020 #36

在我之前给出错误的情况下:from table_name ..... INTO OUTFILE .....

子句的意外排序。(在位置 10 的“FROM”附近)

什么对我有用:

SELECT *
INTO OUTFILE '/Volumes/Development/sql/sql/enabled_contacts.csv'
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
LINES TERMINATED BY '\n'
FROM table_name
WHERE column_name = 'value'

评论

0赞 SteJ 7/15/2022
这就是我一直在寻找的,但请注意,许多现代MySQL服务器都设置了运行--secure-file-priv,这可能会阻止它工作
1赞 Archie 4/9/2021 #37

这避免了必须将输出写入文件,只需要安装,正确转义值,并为 null 值输出空字符串(而不是文字)。expatNULL

你告诉MySQL以XML格式输出结果(使用标志),然后通过下面的C程序将结果管道化。--xml

这也应该非常接近最快的方法。


// mysql-xml-to-csv.c

#include <assert.h>
#include <ctype.h>
#include <err.h>
#include <expat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

/*
    Example of MySQL XML output:

    <?xml version="1.0"?>

    <resultset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" statement="SELECT id as IdNum, lastName, firstName FROM User">
        <row>
            <field name="IdNum">100040</field>
            <field name="lastName" xsi:nil="true"/>
            <field name="firsttName">Cher</field>
        </row>
    </resultset>
*/

#define BUFFER_SIZE     (1 << 16)

// These accumulate the first row column names and values until first row is entirely read (unless the "-N" flag is given)
static XML_Char **column_names;
static size_t num_column_names;
static XML_Char **first_row_values;
static size_t num_first_row_values;

// This accumulates one column's value
static XML_Char *elem_text;                     // note: not nul-terminated
static size_t elem_text_len;

// Flags
static int first_column;
static int reading_value;

// Expat callback functions
static void handle_elem_start(void *data, const XML_Char *el, const XML_Char **attr);
static void handle_elem_text(void *userData, const XML_Char *s, int len);
static void handle_elem_end(void *data, const XML_Char *el);

// Helper functions
static void output_csv_row(XML_Char **values, size_t num);
static void output_csv_text(const char *s, size_t len);
static void add_string(XML_Char ***arrayp, size_t *lengthp, const XML_Char *string, size_t len);
static void add_chars(XML_Char **strp, size_t *lenp, const XML_Char *string, size_t nchars);
static size_t xml_strlen(const XML_Char *string);
static void free_strings(XML_Char ***arrayp, size_t *lengthp);
static void usage(void);

int
main(int argc, char **argv)
{
    char buf[BUFFER_SIZE];
    int want_column_names = 1;
    XML_Parser p;
    FILE *fp;
    size_t r;
    int i;

    // Parse command line
    while ((i = getopt(argc, argv, "hN")) != -1) {
        switch (i) {
        case 'N':
            want_column_names = 0;
            break;
        case 'h':
            usage();
            exit(0);
        case '?':
        default:
            usage();
            exit(1);
        }
    }
    argv += optind;
    argc -= optind;
    switch (argc) {
    case 0:
        fp = stdin;
        break;
    case 1:
        if ((fp = fopen(argv[0], "r")) == NULL)
            err(1, "%s", argv[0]);
        break;
    default:
        usage();
        exit(1);
    }

    // Initialize arrays for column names and first row values
    if (want_column_names) {
        if ((column_names = malloc(10 * sizeof(*column_names))) == NULL)
            err(1, "malloc");
        if ((first_row_values = malloc(10 * sizeof(*first_row_values))) == NULL)
            err(1, "malloc");
    }

    // Initialize parser
    if ((p = XML_ParserCreate(NULL)) == NULL)
        errx(1, "can't initialize parser");
    XML_SetElementHandler(p, handle_elem_start, handle_elem_end);
    XML_SetCharacterDataHandler(p, handle_elem_text);

    // Process file
    while (1) {
        if ((r = fread(buf, 1, sizeof(buf), fp)) == 0 && ferror(fp))
            errx(1, "error reading input");
        if (XML_Parse(p, buf, r, r == 0) == XML_STATUS_ERROR)
            errx(1, "line %u: %s", (unsigned int)XML_GetCurrentLineNumber(p), XML_ErrorString(XML_GetErrorCode(p)));
        if (r == 0)
            break;
    }

    // Clean up
    XML_ParserFree(p);
    fclose(fp);

    // Done
    return 0;
}

static void
handle_elem_start(void *data, const XML_Char *name, const XML_Char **attr)
{
    if (strcmp(name, "row") == 0)
        first_column = 1;
    else if (strcmp(name, "field") == 0) {
        if (column_names != NULL) {
            while (*attr != NULL && strcmp(*attr, "name") != 0)
                attr += 2;
            if (*attr == NULL)
                errx(1, "\"field\" element is missing \"name\" attribute");
            add_string(&column_names, &num_column_names, attr[1], xml_strlen(attr[1]));
        } else {
            if (!first_column)
                putchar(',');
            putchar('"');
        }
        reading_value = 1;
    }
}

static void
handle_elem_text(void *userData, const XML_Char *s, int len)
{
    if (!reading_value)
        return;
    if (column_names != NULL)
        add_chars(&elem_text, &elem_text_len, s, len);
    else
        output_csv_text(s, len);
}

static void
handle_elem_end(void *data, const XML_Char *name)
{
    if (strcmp(name, "row") == 0) {
        if (column_names != NULL) {
            output_csv_row(column_names, num_column_names);
            output_csv_row(first_row_values, num_first_row_values);
            free_strings(&column_names, &num_column_names);
            free_strings(&first_row_values, &num_first_row_values);
        } else
            putchar('\n');
    } else if (strcmp(name, "field") == 0) {
        if (column_names != NULL) {
            add_string(&first_row_values, &num_first_row_values, elem_text, elem_text_len);
            free(elem_text);
            elem_text = NULL;
            elem_text_len = 0;
        } else
            putchar('"');
        first_column = 0;
        reading_value = 0;
    }
}

static void
output_csv_row(XML_Char **values, size_t num_columns)
{
    int i;

    for (i = 0; i < num_columns; i++) {
        if (i > 0)
            putchar(',');
        putchar('"');
        output_csv_text(values[i], xml_strlen(values[i]));
        putchar('"');
    }
    putchar('\n');
}

static void
output_csv_text(const XML_Char *s, size_t len)
{
    while (len-- > 0) {
        if (*s == '"')
            putchar('"');
        putchar(*s);
        s++;
    }
}

static void
add_string(XML_Char ***arrayp, size_t *lengthp, const XML_Char *string, size_t nchars)
{
    char **new_array;

    if ((new_array = realloc(*arrayp, (*lengthp + 1) * sizeof(**arrayp))) == NULL)
        err(1, "malloc");
    *arrayp = new_array;
    if (((*arrayp)[*lengthp] = malloc((nchars + 1) * sizeof(XML_Char))) == NULL)
        err(1, "malloc");
    memcpy((*arrayp)[*lengthp], string, nchars * sizeof(XML_Char));
    (*arrayp)[*lengthp][nchars] = (XML_Char)0;
    (*lengthp)++;
}

static void
add_chars(XML_Char **strp, size_t *lenp, const XML_Char *string, size_t nchars)
{
    XML_Char *new_array;

    if ((new_array = realloc(*strp, (*lenp + nchars) * sizeof(XML_Char))) == NULL)
        err(1, "malloc");
    *strp = new_array;
    memcpy(*strp + *lenp, string, nchars * sizeof(XML_Char));
    *lenp += nchars;
}

static size_t
xml_strlen(const XML_Char *string)
{
    size_t len;

    len = 0;
    while (string[len] != (XML_Char)0)
        len++;
    return len;
}

static void
free_strings(char ***arrayp, size_t *lengthp)
{
    while (*lengthp > 0)
        free((*arrayp)[--*lengthp]);
    free(*arrayp);
    *arrayp = NULL;
}

static void
usage(void)
{
    fprintf(stderr, "Usage: mysql-xml-to-csv [options] [file.xml]\n");
    fprintf(stderr, "Options:\n");
    fprintf(stderr, "  -N\tDo not output column names as the first row\n");
    fprintf(stderr, "  -h\tShow this usage info\n");
}

对于那些不经常使用 C 语言的人,您可以通过运行以下命令来构建此代码(假设您已经安装了 expat 库):

gcc mysql-xml-to-csv.c -lexpat -o mysql-xml-to-csv

使用 openSUSE 15.2 和 gcc 7.5.0 进行测试。

Update:现在在 github 上作为开源项目提供。

评论

0赞 Peter Mortensen 8/6/2021
这是在什么平台上测试的?操作系统(包括版本)、GCC版本等请通过编辑(更改)您的答案来回复,而不是在评论中(没有“编辑:”,“更新:”或类似内容 - 答案应该看起来像今天写的一样)。
3赞 Ken Arnold 8/4/2021 #38

Python 中的一种简单解决方案,用于写入带有标头的标准格式 CSV 文件并将数据写入流(内存使用率低):

import csv

def export_table(connection, table_name, output_filename):
    cursor = connection.cursor()
    cursor.execute("SELECT * FROM " + table_name)

    # thanks to https://gist.github.com/madan712/f27ac3b703a541abbcd63871a4a56636 for this hint
    header = [descriptor[0] for descriptor in cursor.description]

    with open(output_filename, 'w') as csvfile:
        csv_writer = csv.writer(csvfile, dialect='excel')
        csv_writer.writerow(header)
        for row in cursor:
            csv_writer.writerow(row)

你可以像这样使用它:

import mysql.connector as mysql
# (or https://github.com/PyMySQL/PyMySQL should work but I haven't tested it)

db = mysql.connect(
    host="localhost",
    user="USERNAME",
    db="DATABASE_NAME",
    port=9999)

for table_name in ['table1', 'table2']:
    export_table(db, table_name, table_name + '.csv')

db.close()

为简单起见,这故意不包括来自另一个答案的一些更花哨的东西,例如使用环境变量作为凭据等。那里提到了关于线尾的微妙之处,我没有评估过。contextlib

2赞 Majid Fouladpour 12/19/2021 #39

这是肮脏和丑陋的。它只适用于您拥有的只是一个 PHP-*-admin 并且服务器正在运行该选项的特定情况,因此您不能在查询中使用该子句。--secure-file-privINTO OUTFILE '/path/to/export.csv'

您可以做的是解析CSV行...等待它, ,然后复制结果并将它们粘贴到文件中。CONCAT

下面是一个示例,我需要 SQL 格式(将其调整为 CSV 是微不足道的):

SELECT CONCAT(
"('",
`username`, "', '",
`password`, "', '",
`first_name`, "', '",
`last_name`, "', '",
`gender`, "'),"
) AS `row` 
FROM `users`
WHERE `role` != 'Not set'
AND `user_status` = 'Active'
ORDER BY `role`, `gender`, `user_id`
LIMIT 200

这提供了很好的、准备好的导入输出,类似于以下内容:

('jane', '3d7ff...', 'Jane', 'Doe', 'Female'),  
('john', 'd2a33...', 'John', 'Doe', 'Male'),
...
-2赞 MaxV 9/30/2023 #40

非常简单的方法:

  • 只需在屏幕上复制标准MySQL输出即可
  • 然后打开 Libreoffice Calc(或 Excel)
  • 在 Windows 提示符下,将竖线 char | 设置为字段分隔符 char