57 COPY
COPY 是 PostgreSQL 的批量数据导入/导出命令。它以 CSV、文本或二进制格式,在表与文件(或标准输入/输出)之间移动行。与 INSERT 相比,COPY 快得多——有时能快 10–100 倍——因为它跳过了大量逐行开销。要装载大量数据时,优先使用 COPY。
本章要点
- 使用
COPY ... TO导出,以及使用COPY ... FROM导入 COPY(服务端)与\copy(客户端)的区别- CSV 格式以及
HEADER、QUOTE、DELIMITER选项 - 通过
STDIN/STDOUT进行流式传输 - 为什么批量装载时
COPY胜过INSERT
示例
COPY ... TO STDOUT 会写入连接的输出流。使用 psql -f 运行 SQL 文件时,这个流就是你的终端——很适合临时导出。可以从 Shell 重定向输出,将结果保存到文件:psql -f source/copy.sql postgres > out.csv。
COPY ... FROM <path> 会从服务器上的文件读取数据。PostgreSQL 进程需要拥有读取权限;在托管云数据库上,你通常没有这种权限。客户端一侧的 \copy(一种 psql 元命令)会从你的机器读取文件,通过连接将其传输,并在服务器上插入——这与 COPY FROM STDIN 的行为完全相同,只是通过一个便捷封装来实现。
对于编写批量装载代码的应用,几乎每个驱动都提供“COPY FROM STREAM” API(psycopg 的 copy_from、pgx 的 CopyFrom 等)。装载几千行时,它比分批执行的 INSERT 快得多;对于数百万行,它通常是最佳起点,不过收益取决于工作负载。
COPY FROM 会在目标表上调用触发器,并强制执行检查约束和外键约束。触发器的工作因此会计入装载成本。它不会调用规则。暂存表适合在将数据插入最终表之前,对装载内容进行验证和转换。
运行示例
运行方式:
常见陷阱
- 混淆
COPY和\copy:前者在服务器上运行(路径是服务器路径,权限是服务器权限);后者在客户端运行(路径是本地路径)。一个常见错误是使用COPY ... FROM '~/file.csv',然后收到“permission denied”,因为服务器无法读取你的主目录。 - 编码不匹配:文件使用
ENCODING选项指定的编码;如果没有该选项,则使用当前客户端编码。编码错误的 CSV 会产生乱码或“invalid byte sequence”错误。使用WITH (ENCODING 'utf8'),或者事先进行转换。 - 包含嵌入换行符的 CSV 需要
QUOTE和ESCAPE选项。默认值(两者都是")通常可以正常工作。 - 在大规模装载中使用带约束的
COPY会很慢。一个技巧是:先装载到暂存表,去重/校验,然后再通过INSERT ... ON CONFLICT插入目标表。
对于非常大的导入,在 COPY 前删除索引(主键除外),然后重新创建。这样 COPY 会更快,而且最后一次性建立索引,比在数百万行上逐行维护索引更快。将操作包在一个事务中,这样失败时就能回滚。
将 fruits 导出到文件:psql -f source/copy.sql postgres > out.csv。然后将它重新导入暂存表:在一个 psql 会话中创建 fruits_stage,再单独占一行运行 \copy:
比较行数。
下载
SQL 文件:copy.sql · 全部示例文件 · 准备工作