如何以 CSV 格式输出 MySQL 查询结果?
Posted
技术标签:
【中文标题】如何以 CSV 格式输出 MySQL 查询结果?【英文标题】:How can I output MySQL query results in CSV format? 【发布时间】:2018-05-20 03:44:14 【问题描述】:有没有一种简单的方法可以从 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
如果有很多列需要用引号括起来,或者结果中有引号需要转义,就会变得混乱。
【问题讨论】:
您可以在查询中使用REPLACE()
来转义引号。
看看我在 [this ***][1] [1] 中的回答:***.com/questions/12242772/…
你可以Format Table Data as Text table。
这个 *** 问题的公认答案可能是最好的方法:***.com/questions/3760631/mysql-delimiter-question
我在 MariaDB 错误跟踪器 (jira.mariadb.org/browse/MDEV-12879) 上写了一个功能请求,您可以投票。
【参考方案1】:
这又脏又丑。它仅适用于您只有一个 php-*-admin 并且服务器使用 --secure-file-priv
选项运行的特殊情况,因此您不能在查询中使用 INTO 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】:这避免了将输出写入文件,只需要安装expat
,正确转义值,并为空值输出空字符串(而不是文字NULL
)。
您告诉 MySQL 以 XML 格式输出结果(使用 --xml
标志),然后通过下面的 C 程序通过管道传输结果。
这也应该非常接近于最快的方法。
// 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:
现在以open source project on github 的形式提供。
【讨论】:
这是在什么平台上测试的?操作系统(含版本)、GCC 版本等。请通过editing (changing) your answer 回复,不在 cmets 中(without "Edit:", "Update:",或类似的 - 答案应该看起来好像是今天写的)。【参考方案3】:到目前为止,除the MySQL Workbench one 之外的所有解决方案对于 MySQL 数据库中的至少一些可能的内容都是不正确的并且很可能不安全(即安全问题)。
MySQL Workbench(以及类似的phpMyAdmin)提供了形式上正确的解决方案,但它们旨在将输出下载到用户的位置。它们对于自动数据导出之类的事情不是那么有用。
无法从mysql -B -e 'SELECT ...'
的输出中生成可靠正确的 CSV 内容,因为这无法对字段中的回车和空格进行编码。 mysql
的“-s”标志确实会进行反斜杠转义,并且可能会导致正确的解决方案。但是,使用脚本语言(一种具有良好内部数据结构的语言,而不是 Bash)和已经仔细解决编码问题的库要安全得多。
我曾想过为此编写一个脚本,但当我想到我会如何称呼它时,我突然想到要搜索已有的同名作品。虽然我还没有彻底了解它,但mysql2csv 看起来很有希望。根据您的应用程序,指定 SQL 命令的 YAML 方法可能会或可能不会吸引人。对于我的Ubuntu 12.04(Precise Pangolin)笔记本电脑或Debian 6.0(Squeeze)服务器标配的更新版本的Ruby,我也不感到兴奋。是的,我知道我可以使用 RVM,但我不想为了这么简单的目的而维护它。
【讨论】:
你是对的,对于表中的复杂字符串,你必须使用一些像样的库,而不仅仅是 bash。我认为 nodejs 对这种操作有更好的解决方案。像这样example 嗨,好答案,我会在***.com/a/35123787/1504300下面添加一个python解决方案的链接,效果很好,也很简单。我试图编辑你的帖子,但编辑被拒绝了 Rob Miller 的 mysql2csv 脚本坚持通过网络或套接字连接到数据库本身,并且不能用作 unix 样式的管道。也许这是必需的,但它确实限制了使用。 @chrisinmtown 你想输入什么? myslqldump 的输出可能吗?正如我所指出的,mysql -B
的输出无法修复。
@mc0e 我想通过管道 in 输出 mysqldump 的输出,将其读取为 stdin。【参考方案4】:
以 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);
?>
【讨论】:
不错,干净,运行迅速——如果你可以在那个环境中运行 PHP,这是一个很好的解决方案!【参考方案5】: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()
为简单起见,这故意不包括来自another answer 的一些更高级的东西,例如使用环境变量作为凭据,contextlib
等。那里提到了我尚未评估的行尾的微妙之处。
【讨论】:
【参考方案6】:在我的情况下,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'
【讨论】:
【参考方案7】:站在 of 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)
使用该打印语句来说服自己它正在正确解析您的输入:)
一个主要警告:处理回车字符,^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?
请不要@我说我应该使用通用文件模式通过重新打开 sys.stdin.fileno 模式'rU'。我试过了,它会导致嵌入的 \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 选择和上面的 Python 脚本的管道示例:
mysql -u user db --execute="select * from table where id=12345" \
| sed -e 's/\r/\\n/g' \
| mysqlTsvToCsv.py
经过一段时间的斗争,我认为 Python 不是正确的解决方案。如果你能忍受Perl,我认为the one-liner script offered by artfulrobot可能是最有效和最简单的解决方案。
【讨论】:
【参考方案8】:如果您在尝试导出文件时遇到此错误
ERROR 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])
【讨论】:
欢迎来到 Stack Overflow!这不能回答问题。请查看***.com/help/how-to-answer。【参考方案9】:对于那些可能想要以 CSV 格式下载查询结果但不能访问服务器文件但访问数据库的人。
首先,它不是 Linux 命令。步骤如下:
-
使用查询创建视图。例如:(
Create VIEW v as (Select * from user where status = 0)
)
视图将在数据库的view
部分下创建。
现在将视图导出为CSV
。
如果您需要表格列作为CSV的标题,请将Export method:
设置为Custom - display all possible options
并勾选Put columns names in the first row
。
【讨论】:
【参考方案10】:我遇到了同样的问题,Paul's Answer 不是一个选项,因为它是Amazon RDS。用逗号替换制表符不起作用,因为数据已嵌入逗号和制表符。我发现mycli 是 mysql-client 的替代品,它支持带有 --csv
标志的开箱即用的 CSV 输出:
mycli db_name --csv -e "select * from flowers" > flowers.csv
【讨论】:
就像一个魅力,你可以通过:brew update && brew install mycli
在 macOS 中安装 mycli。我再也不会使用股票 mysql 客户端了,mycli 是如此合法!
Mycli 和它的表弟 pgcli 都很棒。我来这里是为了添加这个技巧 mysql 因为我没有得到其他解决方案的任何地方。使用 mysql 干净地输出到本地 .csv 非常困难。
太棒了,很遗憾这个答案很少得到“喜欢”。这是唯一符合我需求的解决方案。除了这个,所有其他的对我来说都不是完美的。我在列中有换行符 - 打开文件时导致 Excel 程序中出现新行。
如果您在使用 1.8.1 版本时遇到问题(可能是因为您使用的是仍然有 Python 2.x 的旧操作系统),则此答案将不起作用,因为“--csv”是'在该版本中不可用。【参考方案11】:
此解决方案将 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)
【讨论】:
不只是第一个名为“query.sh”的文件吗?【参考方案12】:如果您收到secure-file-priv
的错误,那么在将您的目标文件位置转移到C:\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)
这行得通!
例子:
SELECT * FROM admission INTO OUTFILE 'C:/ProgramData/MySQL/MySQL Server 8.0/Uploads/FileName.csv' FIELDS TERMINATED BY ',' ENCLOSED BY '"' LINES TERMINATED BY '\n';
每次运行查询成功,每次都会生成新的CSV文件! 很酷,对吧?
【讨论】:
在 Windows 上,大概?它测试了什么,包括。版本(Windows、MySQL 等)?【参考方案13】:如果您在生产或任何其他无法访问文件系统的服务器上,您可以使用这个简单的技巧和一点点手动操作来获得您想要的。
第 1 步。只需将 CONCAT
下的所有列包装起来,并使用 MySQL 提供的 as 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 文件即可。
【讨论】:
@MajidFouladpour,同意【参考方案14】:以下生成制表符分隔且有效的 CSV 输出。与大多数其他答案不同,此技术可以正确处理制表符、逗号、引号和新行的转义,而无需任何流过滤器,例如 sed、AWK 或 tr。
该示例展示了如何使用流将远程 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() 函数。
【讨论】:
【参考方案15】:什么对我有用:
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;
【讨论】:
Re "...漂亮的 JSON 数据...":漂亮在什么方面?【参考方案16】:以下 Bash 脚本适用于我。它还可以选择获取所请求表的架构。
#!/bin/bash
#
# Export MySQL data to CSV
#https://***.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
【讨论】:
【参考方案17】:您可以在 SQL 编辑器/终端中使用以下命令:
mysql -h(hostname/IP>) -u(username) -p(password) databasename <(query.sql) > outputFILE(.txt/.xls)
例如,
主机名-x.x.x.x
uname - 用户名
密码 - 密码
DBName - 员工数据库
queryFile - employee.sql
outputFile - outputFile.xls
mysql -hx.x.x.x -uusername -ppassword employeeDB
确保您是从 SQL 查询所在的目录执行命令,或在上述命令中提及 SQL 查询位置的完整路径。
【讨论】:
【参考方案18】:如果您在服务器上设置了 PHP,则可以使用 mysql2csv 导出(实际有效的)CSV 文件以进行任意 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
(下载 gist 的内容,检查校验和并使其可执行。)
【讨论】:
【参考方案19】:受 Tim Harding's answer 启发的用于对 CSV 转储进行简单查询的微型 Bash 脚本。
#!/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"
【讨论】:
这使用忽略嵌入双引号字符的 sed 咒语。建议改用 perl 解决方案。【参考方案20】:此页面上的许多答案都很薄弱,因为它们无法处理 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 输入格式的极端情况。
这可以通过流式处理改进以处理非常大的文件。
【讨论】:
更可靠的解决方案是使用 Python 实际连接到数据库,然后您应该可以更轻松地执行处理更大数据集所需的操作(分块结果、流式传输等) . @JoshRumbut,真的来晚了,但我发了***.com/a/41840534/2958070 来补充您的评论 请参阅***.com/questions/356578/… 了解此脚本的扩展版本,其输入方言可处理嵌入的逗号和双引号字符! 它的要点是什么?使用图书馆?内置库?可以link to documentation等吗? (但没有“编辑:”、“更新:”或类似的 - 答案应该看起来好像是今天写的。)。 流媒体方法需要什么?你能在你的答案中详细说明一下吗? (但没有“编辑:”、“更新:”或类似的 - 答案应该看起来好像是今天写的。)【参考方案21】:这救了我几次。它速度快而且效果好!
--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
【讨论】:
【参考方案22】:在命令行中,您可以这样做:
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*
致谢: Exporting table from Amazon RDS into a CSV file
【讨论】:
那是一个疯狂的 oneliner 。帽子【参考方案23】:不完全是 CSV 格式,但来自 MySQL client 的 tee
command 可用于将输出保存到 local 文件中:
tee foobar.txt
SELECT foo FROM bar;
您可以使用notee
禁用它。
SELECT … INTO OUTFILE …;
的问题在于它需要在服务器上写入文件的权限。
【讨论】:
如果使用 .csv 扩展名而不是 .txt,是否有任何格式问题需要注意? @myidealab 格式问题源于逗号等未转义。 CSV 是纯文本格式,因此只需更换扩展名就不会出现格式问题。【参考方案24】:使用the solution posted by 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
的文件【讨论】:
嵌入的双引号字符呢?他们也需要被转义,但我在 sed 咒语中看不到他们的模式。【参考方案25】:这是我的工作:
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 方面做得很好。
【讨论】:
这很棒。轻微的改进可能是引用除数字之外的所有内容perl -F"\t" -lane 'print join ",", map s/"/""/g; /^\d+(?:\.\d+)?$/ ? $_ : qq("$_") @F '
- 你的不会引用 1.2.3
这应该是接受的解决方案恕我直言,当然@artfulrobot 的改进。【参考方案26】:
通过 'tr' 管道(仅限 Unix/Cygwin):
mysql <database> -e "<query here>" | tr '\t' ',' > data.csv
注意:这既不处理嵌入的逗号,也不处理嵌入的标签。
【讨论】:
我很惊讶这个解决方案的投票率如此之低。目前,***解决方案需要许多 db 用户没有的特权(并且有充分的理由;管理员将其提供给它是一种安全风险。)您的解决方案在这里没有任何特殊特权,也可以改进以解决逗号或制表符的缺点,可能在查询本身中进行了替换。 这是一个绝妙的解决方案,效果很好。【参考方案27】:MySQL Workbench 可以将记录集导出为 CSV,而且它似乎很好地处理了字段中的逗号。 CSV 在 OpenOffice Calc 中打开。
【讨论】:
感谢一百万大卫。在花了 3 个小时让换行符正确输出数据中的 HTML 内容后,我使用了 MySQL Workbench,并在 2 分钟内准备好了我的 CSV 文件。 我刚刚发现它也可以保存为 XML,这很棒。我希望通过使用 XSLT 将此 XML 转换为适合导入目标应用程序的 CSV 文件来从一个应用程序迁移到另一个应用程序。 mysql 工作台是导入导出功能的最佳选择。 好吧,但是每次工作台将选择记录限制为 1000 条,当涉及到更多记录时,它就不能很好地工作,如果我尝试导入相同的条件,它通常会被阻止通过workbench将一个比较大的csv文件导入mysql数据库。 我刚刚使用 mysql 工作台成功导出了超过一百万行,所以大文件似乎不是问题。您只需要确保在运行查询之前删除选择限制,并且您可能还必须增加 my.ini 文件中的以下值:max_allowed_packet = 500M, net_read_timeout = 600, net_write_timeout = 600【参考方案28】:如果您使用的机器上安装了 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 来纠正的。
【讨论】:
远非不必要的复杂,这是这里唯一朝着正确方向发展的解决方案——尽管需要做更多的工作。 CSV 比最初看起来更复杂,并且您犯了各种错误。例如,原文中的反斜杠。最好使用已经解决了写入 CSV 的所有问题的库。 @mc0e 通常你要么加倍引号,要么转义引号。我决定加倍引号,因此我不需要转义字符。不同的软件对 CSV 细节有不同的想法。例如,MySQL 中的 LOAD DATA 确实将 \ 视为转义字符,而 Open Office Calc 则不会。当我写答案时,我正在将数据导出到电子表格中。 我的代码处理了导出 my 数据集所需的一切;)您的回答也是朝着正确方向迈出的一步,但正如第一条评论所说,它应该 1) 直接从脚本连接到 sql 数据库以及 2) 使用适当的 csv 库。【参考方案29】:用途:
mysql your_database -p < my_requests.sql | awk 'print $1","$2' > out.csv
【讨论】:
我真的很喜欢这个。它更干净,我喜欢使用 awk。但是,我可能会选择这个:mysql -uUser -pPassword your_database < my_requests.sql | awk 'BEGINOFS="="; print $1,$2' > out.csv
这对于带有空格的字段值会失败。【参考方案30】:
这是一种相当粗糙的方法[1]:
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/// 表示将第一个之间的内容替换为 // 与第二个之间的内容 // 末尾的“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] 我在某个地方找到了它,无法获得任何功劳。
【讨论】:
以上是关于如何以 CSV 格式输出 MySQL 查询结果?的主要内容,如果未能解决你的问题,请参考以下文章
如何以 csv 格式输出 MySQL 查询结果(到屏幕,而不是文件)?