使用 MySQL 导入和导出 TSV

Importing and exporting TSVs with MySQL

我正在使用 MySQL 5.7 的数据库,有时,需要结合使用脚本和手动编辑来更新数据。因为使用数据库的人通常不熟悉 SQL,我想将数据导出为 TSV,然后可以对其进行操作(例如使用 Python 的 pandas模块),然后被导入回来。我假设标准方法是直接连接到数据库,但我认为在这种情况下使用 TSV 有一些好处。我一直在阅读 MySQL 文档和一些 Whosebug 问题,以找到执行此操作的最佳方法。我找到了几个解决方案,但是,它们都有点不方便。我将在下面列出它们并解释我的问题。

我的问题是: 我是否遗漏了什么,例如一些有用的 SQL 命令或 CLI 选项来帮助解决这个问题?还是我发现的解决方案在 importing/exporting TSV 时已经是最好的?

我的示例数据库如下所示:

数据库:Export_test

Table: 样本

Field Type Null Key
id int(11) NO PRI
text_data text NO
optional int(11) YES
time timestamp NO

示例数据:

INSERT INTO `Sample` VALUES (1,'first line\\nsecond line',NULL,'2022-02-16 20:17:38');

数据包含转义的换行符,这在导出时给我带来了很多问题。

Table:参考

Field Type Null Key
id int(11) NO PRI
foreign_key int(11) NO MUL

示例数据:

INSERT INTO `Reference` VALUES (1,1);

foreign_key 引用 Sample.id.

关于编码的注意事项:作为对试图做同样事情的人的警告:如果你想要 export/import 数据,请确保 characters sets and collations已正确设置连接。这让我有些头疼,因为虽然数据本身是 utf8mb4,但客户端、服务器和连接字符集是 latin1,这在某些情况下会导致一些数据丢失。

导出

所以,对于导出,我基本上找到了三种解决方案,它们的行为都有些不同:

A:SELECT 标准输出重定向

mysql Export_test -e "SELECT * FROM Sample;" > out.tsv 

输出:

id  text_data   optional    time
1   first line\\nsecond line   NULL    2022-02-16 21:26:13

优点:

缺点:

解决方法:编辑数据时替换 NULL

乙:SELECT INTO OUTFILE

mysql Export_test -e "SELECT * FROM Sample INTO OUTFILE '/tmp/out.tsv';"

输出:

1   first line\\
second line \N  2022-02-16 21:26:13

优点:

缺点:

解决方法:手动修复换行符;手动添加 headers 或在脚本中提供它们;使用 /tmp/ 作为输出目录

C: mysqldump--tab(在幕后执行 SELECT INTO OUTFILE

mysqldump --tab='/tmp/' --skip-tz-utc Export_test Sample

输出、优缺点:与导出变体 B 相同

需要注意的一点:如果使用--skip-tz-utc,输出只与B相同;否则,时间戳将转换为 UTC,并在导入数据后关闭。

导入

我首先没有意识到的是,仅使用 LOAD INTO or mysqlimport, although that's something many GUI tools appear to be doing and other people attempted 直接更新数据是不可能的。对于我这个初学者来说,MySQL 文档并不能立即说明这一点。解决方法似乎是创建一个空的 table,将数据导入那里,然后通过连接更新实际感兴趣的 table。我还认为可以用这个更新单独的列,这又是不可能的。如果还有其他方法可以实现,我很想知道。

据我所知,有两个选项,它们的作用几乎相同:

加载到:

mysql Export_test -e "SET FOREIGN_KEY_CHECKS = 0; LOAD DATA INFILE '/tmp/Sample.tsv' REPLACE INTO TABLE Sample IGNORE 1 LINES; SET FOREIGN_KEY_CHECKS = 1;"

mysqlimport(在幕后执行LOAD INTO):

mysqlimport --replace Export_test /tmp/Sample.tsv

注意:如果有像本例这样的外键约束,需要执行SET FOREIGN_KEY_CHECKS = 0;(据我所知,在这种情况下不能直接使用mysqlimport) .此外,如果输入 TSV 包含 header,IGNORE 1 LINES--ignore-lines 可用于跳过第一行。对于 mysqlimport,不带扩展名的输入文件的名称必须是 table 的名称。同样,文件读取权限可能是一个问题,/tmp/ 用于避免这种情况。

有没有办法让这个过程更方便?比如,有没有一些我可以使用的选项来避免手动解决方法,或者有没有办法使用 TSV 导入到 UPDATE 条目而不创建临时 table?

我最后做的是使用 LOAD INTO OUTFILE 导出,手动添加 header 并手动修复格式错误的行。操作数据后,我使用 LOAD DATA INTO 更新数据。在另一种情况下,我使用 SELECT 导出到 stdout 重定向,操作数据,然后添加一个脚本,该脚本只创建了一个文件,其中包含一堆 UPDATE ... WHERE 语句和相应的数据。然后我 运行 结果 .sql 在我的 da卑鄙。在这种情况下,后者可能是最佳选择吗?

在 MySQL 中导出和导入确实有点笨拙。

一个问题是它引入了竞争条件。如果您导出数据来处理它,然后有人修改了数据库中的数据,然后您导入修改后的数据,覆盖了您朋友最近的更改,该怎么办?

如果您说“在您 re-import 数据之前,不允许任何人更改数据”,如果 table 很大,这可能会导致客户端被阻塞的时间过长,无法接受。

趋势是人们希望数据库将停机时间降至最低,理想情况下完全没有停机时间。数据库工具的进步通常是考虑到这一优先事项,而不是为了适应从 MySQL 中提取数据进行转换的工作流程。

此外,如果数据库足够大以至于导出的数据出现问题怎么办,因为您将 500GB 的 TSV 文件存储在哪里? pandas 甚至可以处理这么大的文件吗?

大多数人所做的是修改保留在数据库中的数据。他们使用 in-place UPDATE 语句来修改数据。如果他们不能一次完成此操作(例如,二进制日志事件的实际限制为 4GB),那么他们会更新更多 modest-size 行子集,循环直到他们转换了所有行上的数据给定的 table.