使用 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
优点:
添加了- header,方便与外部程序一起使用
- 格式化工作正常
缺点:
NULL
用于空值;导入时,需要 \N
;据我所知,这不能配置出口
解决方法:编辑数据时替换 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
优点:
\N
用于空数据
缺点:
- 转义换行符处理不正确
- header缺少
- 文件写入权限问题
解决方法:手动修复换行符;手动添加 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.
我正在使用 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
优点:
-
添加了
- header,方便与外部程序一起使用
- 格式化工作正常
缺点:
NULL
用于空值;导入时,需要\N
;据我所知,这不能配置出口
解决方法:编辑数据时替换 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
优点:
\N
用于空数据
缺点:
- 转义换行符处理不正确
- header缺少
- 文件写入权限问题
解决方法:手动修复换行符;手动添加 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.