将 MySQL 命令行结果的输出格式更改为 CSV

Posted

技术标签:

【中文标题】将 MySQL 命令行结果的输出格式更改为 CSV【英文标题】:Change output format for MySQL command line results to CSV 【发布时间】:2013-03-16 10:31:12 【问题描述】:

我想在命令行上从查询输出到 mysql 获取无标题 CSV 数据。我在与 MySQL 服务器不同的机器上运行此查询,因此所有那些带有“INTO OUTFILE”的 Google 答案都不好。

所以我运行mysql -e "select people, places from things"。输出的东西看起来有点像这样:

+--------+-------------+
| people | places      |
+--------+-------------+
|   Bill | Raleigh, NC |
+--------+-------------+

好吧,那不好。但是,嘿,看!如果我只是将它传递给 anything,它会将它变成一个制表符分隔的列表:

people  places
Bill    Raleigh, NC

这样更好——至少它可以通过编程方式解析。但我不想要 TSV,我想要 CSV,也不想要那个标题。我可以用mysql <stuff> | tail -n +2 去掉标题,但如果MySQL 只是有一个标志可以省略它,我想避免这种麻烦。而且我不能只用逗号替换所有选项卡,因为它不能处理包含逗号的内容。

那么,我怎样才能让 MySQL 省略标头并以 CSV 格式提供数据?

【问题讨论】:

【参考方案1】:

作为部分答案:mysql -N -B -e "select people, places from things"

-N 告诉它不要打印列标题。 -B 是“批处理模式”,使用制表符分隔字段。

如果制表符分隔值不够用,请参阅this *** Q&A。

【讨论】:

【参考方案2】:

上述解决方案仅适用于特殊情况。在一般情况下,嵌入逗号、嵌入引号以及其他使 CSV 难以处理的事情会让您自己陷入各种麻烦。

帮自己一个忙,使用通用解决方案 - 做对了,您就不必再考虑它了。一个非常强大的解决方案是csvkit 命令行实用程序 - 通过 Python 可用于所有操作系统。通过pip install csvkit 安装。这将为您提供正确的 CSV 数据:

    mysql -e "select people, places from things" | csvcut -t

这会生成逗号分隔的数据,而标题仍然存在。删除标题行:

    mysql -e "select people, places from things" | csvcut -t | tail -n +2

这会产生 OP 请求的内容。

【讨论】:

正是我想要的!并且(基于***.com/questions/356578/…)您可以同时生成一个本地CSV文件,例如:mysql -e "select people, places from things" | csvcut -t > output.csv【参考方案3】:

我最后写了my own command-line 工具来解决这个问题。它类似于cut,除了它知道如何处理引用的字段等。这个工具与@Jimothy 的答案配对,允许我从我没有文件系统访问权限的远程 MySQL 服务器获取无标题的 CSV 到我的本地机器上用这个命令:

$ mysql -N -e "select people, places from things" | csvm -i '\t' -o ','
Bill,"Raleigh, NC"

csvmaster on github

【讨论】:

编写自定义实用程序并不能真正帮助那些不能(或不想)下载和构建它的人。我认为另一个答案更实用。 没错,内置解决方案通常更可取。但是,该问题专门要求分隔符是逗号,而不是制表符,并且没有内置的方法可以做到这一点。 Jimothy 的回答只涵盖了问题的一半。【参考方案4】:

这是如何在客户端将结果保存到 CSV 而无需额外的非标准工具。 此示例仅使用mysql 客户端和awk

单行:

mysql --skip-column-names --batch -e 'select * from dump3' t | awk -F'\t'' sep=""; for(i = 1; i

需要做什么的逻辑解释

    首先,让我们看看数据在 RAW 模式下的样子(使用--raw 选项)。数据库和表分别是tdump3

    您可以看到从“新行”(第一行)开始的字段被分成三行,因为值中放置了新行。

mysql --skip-column-names --batch --raw -e 'select * from dump3' t 一行 2 新行 引号 " 反斜杠 \ 两个引号 "" 两个反斜杠 \\ 两个制表符换行 场的尽头 另一行 1 另一行描述,没有任何特殊字符
    批量输出数据(不带--raw选项)-每条记录通过转义字符(如\<tab>new-lines)变为单行文本
mysql --skip-column-names --batch -e 'select * from dump3' t 一行 2 新行\n引号 " 反斜杠 \\ 两个引号 "" 两个反斜杠 \\\\ 两个制表符\t\t新行\n字段结尾 另一行 1 另一行描述,没有任何特殊字符
    CSV格式的数据输出

提示是将数据保存为带有转义字符的 CSV 格式。

这样做的方法是将mysql --batch 生成的特殊实体(\t 作为制表符,\\ 作为反斜杠,\n 作为换行符)转换为每个值(字段)的等效字节。 然后整个值被" 转义并被" 包围。 顺便说一句 - 使用相同的字符进行转义和封闭可以轻轻地简化输出和处理,因为您没有两个特殊字符。 出于这个原因,您对值所做的所有事情(从 csv 格式的角度来看)就是将 " 更改为 "" whithin 值。以更常见的方式(分别转义和封闭\"),您必须首先将\ 更改为\\,然后将" 更改为\"

以及命令的逐步解释

# 我们产生单行输出,如步骤 2 所示。 mysql --skip-column-names --batch -e 'select * from dump3' t # 将字段分隔符设置为,因为 mysql 以这种方式生成 | awk -F'\t' # 这开始从 mysql 数据中迭代每一行/记录 - awk 的标准行为 ' # 字段分隔符为空,因为我们没有在第一个输出字段之前打印分隔符 sep=""; -- 遍历每个字段并将字段转换为 csv 正确值 for(i = 1; i 对应的字节 gsub(/\\t/, "\t",$i); -- 把 \n 改成新行对应的字节 gsub(/\\n/, "\n",$i); -- 将两个 \\ 变为一个 \ gsub(/\\\\/,"\\",$i); -- 将 value 更改为 CSV 正确的字面值 - 将 " 更改为 "" gsub(/"/, "\"\"",$i); -- 打印由 " 包围的输出字段并在之前添加分隔符 printf sep"\""$i"\""; -- 处理第一个字段后设置分隔符 - 因为之前我们不需要它 sep=","; -- 在处理完最后一个字段后添加新行 - 这表示 csv 记录分隔符 if(i==NF) printf"\n" '

【讨论】:

"--raw" 不转义 \t、\n 和 "。因此,请注意此选项。如果任何列包含此字符,此选项可能会破坏您的结果。 没错,这就是解决方案没有“--raw”选项的原因。它仅在第一步中用于显示准确数据。 遇到了一种情况,即“用完这个”失败 - 看起来 URL 中的百分号是什么让它窒息?我正在使用的 mySQL 转储是公开的(2017 年 1 月的最新转储):ifarchive.org/indexes/if-archiveXinfoXifdb.html - 导致问题的查询:“SELECT * FROM games WHERE id='ju8qpl62enpiutnm';” -- awk 是否需要对添加的百分号进行转义? 我对 % 符号没有任何问题。您能否提供唯一的“mysql”命令的输出,不带 | awk ... ? 这应该被标记为正确答案。救了我的命,谢谢!如果有人遇到百分号问题,只需添加gsub(/%/, "%%", $i);【参考方案5】:

使用 sed 怎么样?它是大多数(所有?)Linux 操作系统的标准配置。

sed 's/\t/<your_field_delimiter>/g'

此示例使用 GNU sed (Linux)。对于 POSIX sed (AIX/Solaris),我相信您会键入文字 TAB 而不是 \t

示例(用于 CSV 输出):

#mysql mysql -B -e "select * from user" | while read; do sed 's/\t/,/g'; done

localhost,root,,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,,,,,0,0,0,0,,
localhost,bill,*2470C0C06DEE42FD1618BB99005ADCA2EC9D1E19,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,,,,,0,0,0,0,,
127.0.0.1,root,,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,,,,,0,0,0,0,,
::1,root,,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,,,,,0,0,0,0,,
%,jim,*2470C0C06DEE42FD1618BB99005ADCA2EC9D1E19,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,,,,,0,0,0,0,,

【讨论】:

如果您的单元格值可以包含制表位,这可能需要小心。此外,如果您的单元格值包含逗号,则此方法将生成具有可变列数的 CSV,而无法识别什么是列分隔逗号以及内容的一部分。像 sedawk 这样的传统 *nix 工具不适合这种情况,因为正确转义内容会很快变得丑陋。 这是可以构建的。如果您的数据包含逗号,您可以使用不同的字段分隔符。绝对不是一个完整的、一刀切的解决方案【参考方案6】:

mysqldump 实用程序可以为您提供帮助,基本上使用--tab 选项,它是SELECT INTO OUTFILE 语句的包装。

例子:

mysqldump -u root -p --tab=/tmp world Country --fields-enclosed-by='"' --fields-terminated-by="," --lines-terminated-by="\n" --no-create-info

这将创建 csv 格式的文件/tmp/Country.txt

【讨论】:

根据 'mysqldump' 手册页,--tab 功能将文件放到 MySQL 服务器上,而不是您的客户端机器上。【参考方案7】:

如果您使用的是 mysql 客户端,您可以为每个会话设置 resultFormat,例如

mysql -h localhost -u root --resutl-format=json

mysql -h localhost -u root --vertical

查看完整的参数列表here。

【讨论】:

【参考方案8】:

mysql客户端可以检测到输出fd,如果fd是S_IFIFO(pipe)则不输出ASCII TABLES,如果fd是字符设备(S_IFCHR)则输出ASCII TABLES。

您可以使用 --table 强制输出 ASCII TABLES,例如:

$mysql -t -N -h127.0.0.1 -e "select id from sbtest1 limit 1" | cat
+--------+
| 100024 |
+--------+

-t, --table 以表格形式输出。

【讨论】:

【参考方案9】:

您可以使用spyql读取mysql的制表符分隔输出并生成逗号分隔的CSV并关闭标题写入:

$ mysql -e "SELECT 'Bill' AS people, 'Raleigh, NC' AS places" | spyql -Oheader=False "SELECT * FROM csv TO csv"
Bill,"Raleigh, NC"

spyql 检测输入是否有标题以及分隔符是什么。默认情况下,输出分隔符是逗号。如果您愿意,可以手动指定所有这些选项:

$ mysql -e "SELECT 'Bill' AS people, 'Raleigh, NC' AS places" | spyql -Idelimiter="'\t'" -Iheader=True -Odelimiter="," -Oheader=False "SELECT * FROM csv TO csv"
Bill,"Raleigh, NC"

我不会关闭 mysql 上的标头写入,因为 spyql 可以利用它,例如,如果您选择生成 JSON 而不是 CSV:

$ mysql -e "SELECT 'Bill' AS people, 'Raleigh, NC' AS places" | spyql "SELECT * FROM csv TO json" 
"people": "Bill", "places": "Raleigh, NC"

或者如果您需要引用您的列:

$ mysql -e "SELECT 'Bill' AS people, 'Raleigh, NC' AS places" | spyql -Oindent=2 "SELECT *, 'I am  and I live in .'.format(people, places) AS message FROM csv TO json"

  "people": "Bill",
  "places": "Raleigh, NC",
  "message": "I am Bill and I live in Raleigh, NC."

免责声明:我是spyql的作者

【讨论】:

以上是关于将 MySQL 命令行结果的输出格式更改为 CSV的主要内容,如果未能解决你的问题,请参考以下文章

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

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

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

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

如何以 csv 格式输出 MySQL 查询结果(到屏幕,而不是文件)?

将mysql查询结果转换为CSV(带复制/粘贴)