COPY
COPY — 在文件和表之间复制数据
大纲
COPYtable_name[ (column_name[, ...] ) ] FROM { 'filename' | PROGRAM 'command' | STDIN } [ [ WITH ] (option[, ...] ) ] COPY {table_name[ (column_name[, ...] ) ] | (query) } TO { 'filename' | PROGRAM 'command' | STDOUT } [ [ WITH ] (option[, ...] ) ] 其中option为以下之一: FORMATformat_nameOIDS [boolean] FREEZE [boolean] DELIMITER 'delimiter_character' NULL 'null_string' HEADER [boolean] QUOTE 'quote_character' ESCAPE 'escape_character' FORCE_QUOTE { (column_name[, ...] ) | * } FORCE_NOT_NULL (column_name[, ...] ) FORCE_NULL (column_name[, ...] ) ENCODING 'encoding_name'
描述
COPY 在
PostgreSQL 表与标准文件系统文件之间传输数据。COPY TO 将表的内容复制到文件,而 COPY FROM
则将数据从文件复制到表中(追加到表中已有的数据之后)。COPY TO 也可以复制
SELECT 查询的结果。
如果指定了列列表,COPY TO 只会将指定列中的数据复制到文件。对于 COPY FROM,文件中的每个字段会按顺序插入到指定列中。未在 COPY FROM 列列表中指定的表列将接收其默认值。
带文件名的 COPY 会指示
PostgreSQL 服务器直接从文件读取或向文件写入。该文件必须可由
PostgreSQL 用户(服务器运行时使用的用户 ID)访问,并且其名称必须从服务器的视角指定。当指定
PROGRAM 时,服务器会执行给定的命令,并从该程序的标准输出读取,或者向该程序的标准输入写入。该命令必须从服务器的视角指定,并且必须可由 PostgreSQL 用户执行。指定
STDIN 或 STDOUT 时,数据通过客户端与服务器之间的连接传输。
参数
table_name一个现有表的名称(可以是模式限定的)。
column_name要复制的可选列列表。如果没有指定列列表,则会复制该表所有列。
query其结果将被复制的 SELECT、VALUES、INSERT、UPDATE 或 DELETE 命令。注意,查询外层必须带圆括号。
对于
INSERT、UPDATE和DELETE查询,必须提供 RETURNING 子句,并且目标关系不能有条件规则,也不能有ALSO规则,也不能有扩展为多个语句的INSTEAD规则。filename输入或输出文件的路径名。输入文件名可以是绝对路径或相对路径,但输出文件名必须是绝对路径。Windows 用户可能需要使用
E''字符串,并将路径名中的任何反斜线写成双反斜线。PROGRAM要执行的命令。在
COPY FROM中,输入从该命令的标准输出读取;而在COPY TO中,输出会写入该命令的标准输入。注意,该命令由 shell 调用,因此如果需要向 shell 命令传递来自不可信来源的参数,必须谨慎移除或转义对 shell 可能有特殊意义的字符。出于安全考虑,最好使用固定命令字符串,或至少避免在其中传入任何用户输入。
STDIN指定输入来自客户端应用。
STDOUT指定输出发送到客户端应用。
boolean指定所选选项是否开启。可以写
TRUE、ON或1来启用选项,写FALSE、OFF或0来禁用它。也可以省略boolean值,此时假定为TRUE。FORMAT选择要读取或写入的数据格式:
text、csv(逗号分隔值)或binary。默认为text。OIDS指定复制每一行的 OID。(如果为没有 OID 的表指定了
OIDS,或者复制的是一个query,就会报错。)FREEZE请求以行已冻结的状态复制数据,就像运行过
VACUUM FREEZE命令之后一样。这是用于初始数据装载的性能选项。只有当正在装载的表是在当前子事务中创建或截断的、没有打开的游标且该事务不持有更早的快照时,行才会被冻结。目前不能对分区表执行COPY FREEZE。注意,一旦数据成功装载,所有其他会话将立即能够看到这些数据。这违反了正常的 MVCC 可见性规则,指定此选项的用户应注意可能由此引发的问题。
DELIMITER指定分隔文件中每一行(记录)内各列的字符。文本格式中默认是一个制表符,而
CSV格式中默认是一个逗号。这必须是一个单一的单字节字符。使用binary格式时不允许这个选项。NULL指定表示一个空值的字符串。文本格式中默认是
\N(反斜线 -N),CSV格式中默认是一个未加引用的空串。在你不想区分空值和空串的情况下,即使在文本格式中你也可能更喜欢空串。使用binary格式时不允许这个选项。注意
在使用
COPY FROM时,任何匹配此字符串的数据项都会被存储为空值,因此应确保这里使用的字符串与COPY TO时使用的相同。HEADER指定文件包含一个标题行,其中列出文件中每一列的名称。输出时,第一行包含表中的列名;输入时,第一行会被忽略。只有使用
CSV格式时才允许此选项。QUOTE指定在对数据值加引号时使用的引用字符。默认是双引号。这必须是一个单一的单字节字符。只有使用
CSV格式时才允许这个选项。ESCAPE指定在与
QUOTE值匹配的数据字符之前应出现的字符。默认值与QUOTE值相同(这样当引用字符出现在数据中时,就会被双写)。这必须是一个单一的单字节字符。只有使用CSV格式时才允许这个选项。FORCE_QUOTE强制对每个指定列中的所有非
NULL值使用引号。NULL输出永远不会加引号。如果指定了*,则所有列中的非NULL值都会加引号。此选项仅允许用于COPY TO,且只能在使用CSV格式时使用。FORCE_NOT_NULL不要将指定列的值与空值串进行匹配。在空值串就是空串的默认情况下,这意味着空串将被读作长度为零的字符串而不是空值(即使它们没有被引用)。只有在
COPY FROM中使用CSV格式时才允许这个选项。FORCE_NULL将指定列的值与空值串匹配,即使它已经被加上引号;如果找到匹配,就将该值设为
NULL。在空值串就是空串的默认情况下,这会把一个带引号的空串转换为 NULL。只有在COPY FROM中使用CSV格式时才允许这个选项。ENCODING指定文件采用
encoding_name编码。如果省略此选项,将使用当前客户端编码。详见下文注解。
输出
成功完成时,COPY 命令会返回形如
COPY count
的命令标签。count 为复制的行数。
注意
只有当命令既不是 COPY ... TO STDOUT,也不是等效的
psql 元命令\copy ... to stdout 时,psql 才会打印这个命令标签。这是为了避免将命令标签与刚刚输出的数据混淆。
注解
COPY TO 只能用于普通表,不能用于视图,且不会复制子表或子分区中的行。例如,COPY 复制的行与 table TOSELECT * FROM ONLY 相同。语法 tableCOPY (SELECT * FROM 可用于导出继承层次结构、分区表或视图中的所有行。table) TO ...
COPY FROM 可用于普通表、外部表、分区表,以及具有
INSTEAD OF INSERT 触发器的视图。
你必须对 COPY TO 读取其值的表具有
SELECT 权限,并对 COPY FROM
插入其值的表具有 INSERT 权限。对于命令中列出的列,具有列级权限即可。
如果对表启用了行级安全,相关的 SELECT 策略将应用于
COPY
语句。目前,对启用了行级安全的表不支持 table TOCOPY FROM。请改用等效的 INSERT 语句。
COPY 命令中指定的文件由服务器而非客户端应用直接读取或写入。因此,这些文件必须位于数据库服务器所在机器上,或者可由数据库服务器访问,而不是仅由客户端访问。它们必须可由 PostgreSQL 用户(服务器运行时使用的用户 ID)访问,并且对该用户可读或可写。同样,使用 PROGRAM 指定的命令也是由服务器而非客户端应用直接执行,因而必须可由 PostgreSQL 用户执行。只有数据库超级用户,或被授予默认角色 pg_read_server_files、pg_write_server_files 或
pg_execute_server_program 之一的用户,才允许使用指定文件名或命令的 COPY,因为这允许读取或写入服务器有权访问的任意文件,或者运行服务器有权执行的程序。
不要将 COPY 与
psql 指令
\copy
混淆。\copy 会调用
COPY FROM STDIN 或 COPY TO
STDOUT,然后在 psql 客户端可访问的文件中读取或存储数据。因此,使用\copy 时,文件的可访问性和访问权限取决于客户端而不是服务器。
建议在 COPY 中使用的文件名始终指定为绝对路径。对于 COPY TO,服务器会强制这一点;但对于
COPY FROM,你仍可选择从使用相对路径指定的文件中读取。该路径将相对于服务器进程的工作目录(通常是集簇的数据目录)而非客户端的工作目录进行解释。
使用 PROGRAM 执行命令可能会受到操作系统的访问控制机制(如 SELinux)的限制。
COPY FROM 将调用目标表上的任何触发器和检查约束。但是它不会调用规则。
对于标识列,COPY FROM 命令总会写入输入数据中提供的列值,其行为类似于 INSERT 的
OVERRIDING SYSTEM VALUE 选项。
COPY 的输入和输出会受
DateStyle 影响。为确保数据能移植到其他可能使用非默认
DateStyle 设置的 PostgreSQL
安装中,使用 COPY TO 前应将
DateStyle 设置为 ISO。同样也建议避免在
IntervalStyle 设置为 sql_standard 时转储数据,因为负的 interval 值可能会被采用不同
IntervalStyle 设置的服务器误解。
即使数据会被服务器直接从一个文件读取或者写入一个文件而不通过客户端,输入数据也会被根据 ENCODING 选项或者当前客户端编码解释,并且输出数据会被根据 ENCODING 或者当前客户端编码进行编码。
COPY 会在遇到第一个错误时停止操作。对于 COPY TO,这应当不会导致问题;但对于 COPY FROM,目标表此时已经接收了前面的行。这些行不可见也不可访问,但仍占用磁盘空间。如果在一次大型复制操作已经进行很久后才失败,可能会浪费大量磁盘空间。可以调用 VACUUM 回收这些浪费的空间。
FORCE_NULL 和 FORCE_NOT_NULL 可以同时用于同一列。这会把带引号的空值串转换为空值,并把不带引号的空值串转换为空串。
文件格式
文本格式
在使用 text 格式时,读取或写入的是一个文本文件,其中表中的每一行对应文件中的一行。每行中的列由分隔符字符隔开。列值本身是由各属性数据类型的输出函数生成或可被其输入函数接受的字符串。对于为空值的列,会使用指定的空值串代替。如果输入文件中的任何一行包含的列数多于或少于预期,COPY FROM 就会报错。如果指定了 OIDS,OID 会作为第一列读取或写入,位于用户数据列之前。
数据结束可以表示为只包含反斜线加点号(\.)的一行。从文件读取时,不需要数据结束标记,因为文件结束已足够;只有在使用 3.0 之前版本的客户端协议、向客户端应用程序复制数据或从其复制数据时,才需要该标记。
在 COPY 数据中,可以使用反斜线字符(\)来转义那些原本可能被当作行或列分隔符的数据字符。特别是,如果下列字符作为列值的一部分出现,那么它们前面必须加一个反斜线:反斜线本身、换行、回车以及当前分隔符字符。
COPY TO 输出指定的空值串时不会添加任何反斜线;相反,COPY FROM 会在去除反斜线之前先将输入与空值串进行匹配。因此,像\N 这样的空值串不会与实际的数据值\N 混淆,因为后者会表示为\\N。
COPY FROM 识别下列特殊的反斜线序列:
| 序列 | 表示 |
|---|---|
\b | 退格 (ASCII 8) |
\f | 换页 (ASCII 12) |
\n | 新行 (ASCII 10) |
\r | 回车 (ASCII 13) |
\t | 制表 (ASCII 9) |
\v | 纵向制表 (ASCII 11) |
\digits | 反斜线后跟一到三个八进制数字表示该数字代码对应的字节 |
\xdigits | 反斜线加 x 后跟一到两个十六进制数字表示该数字代码对应的字节 |
目前,COPY TO 从不会输出八进制或十六进制数字反斜线序列,但对这些控制字符确实会使用上表列出的其他序列。
上表中未提到的其他字符,在前面加了反斜线后仍表示该字符本身。不过,要注意不要不必要地添加反斜线,因为那可能意外地产生与数据结束标记(\.)或空值串(默认是\N)匹配的字符串。这些字符串会在进行任何其他反斜线处理之前先被识别出来。
强烈建议生成 COPY 数据的应用将数据中的换行和回车分别转换为\n 和\r 序列。目前,仍然可以用反斜线加回车表示数据回车,用反斜线加换行表示数据换行。不过,未来版本可能不再接受这些表示方式。如果 COPY 文件在不同机器之间传输(例如从 Unix 到 Windows,或反之),这些表示方式也非常容易被破坏。
所有反斜线序列都在编码转换后进行解释。用八进制和十六进制数字反斜线序列指定的字节必须在数据库编码中形成有效字符。
COPY TO 会用 Unix 风格的换行(“\n”)结束每一行。运行在 Microsoft Windows
上的服务器则会输出回车/换行(“\r\n”),但这只适用于复制到服务器文件的 COPY;为保证跨平台一致性,COPY TO STDOUT 总是发送“\n”,与服务器平台无关。COPY FROM 能够处理以换行、回车或回车/换行结束的行。为减少本应是数据的未加反斜线新行或回车带来的风险,如果输入中的行结束符并不一致,COPY FROM 将会报错。
CSV 格式
这种格式选项用于导入和导出许多其他程序(如电子表格)使用的逗号分隔值(CSV)文件格式。不同于
PostgreSQL 标准文本格式使用的转义规则,它会生成并识别通用的 CSV 转义机制。
每条记录中的值由 DELIMITER 字符分隔。如果某个值包含分隔符字符、QUOTE 字符、NULL 字符串、回车或换行字符,那么整个值都会以前后各一个 QUOTE 字符包围,并且该值内每次出现 QUOTE 字符或 ESCAPE
字符之前都会加上转义字符。对于指定列中的非 NULL 值输出,还可以使用 FORCE_QUOTE 来强制加引号。
CSV 格式没有标准方式区分 NULL 值和空字符串。PostgreSQL 的 COPY 通过引号来处理这一区别。NULL 会按照 NULL 参数字符串输出,且不会被加引号;而与 NULL 参数字符串匹配的非 NULL
值会被加引号。例如,在默认设置下,NULL 会写成一个未加引号的空字符串,而空字符串数据值会写成双引号包围的形式("")。读取值时遵循类似规则。你可以使用 FORCE_NOT_NULL 来阻止对指定列进行 NULL 输入比较。也可以使用 FORCE_NULL
将带引号的空值串数据值转换为 NULL。
因为反斜线在 CSV 格式中不是特殊字符,数据结束标记 \. 也可能作为数据值出现。为避免误解,当 \. 数据值作为一行中的唯一字段出现时,输出时会自动给它加上引号;输入时,如果它带有引号,就不会被解释为数据结束标记。如果正在装载由其他应用程序创建的文件,其中只有一个不带引号的列,而且该列可能包含值 \.,那么可能需要在输入文件中给该值加上引号。
注意
在 CSV 格式中,所有字符都有意义。被空白字符或
DELIMITER 之外其他字符包围的带引号值,会把这些字符也包含进值中。如果你导入的数据来自某个会用空白把 CSV
行填充到固定宽度的系统,这可能导致错误。出现这种情况时,你可能需要在将数据导入 PostgreSQL 之前,先预处理 CSV 文件以移除尾随空白。
注意
CSV 格式既能识别也能生成这样的 CSV 文件:其中带引号的值包含内嵌的回车和换行。因此,这类文件不像文本格式文件那样严格地一行对应表中的一行。
注意
很多程序会生成奇怪、甚至近乎反常的 CSV 文件,因此这种文件格式更像一种约定而非标准。因而你可能会遇到无法用这种机制导入的文件,而 COPY 也可能生成其他程序无法处理的文件。
二进制格式
binary 格式选项会使所有数据以二进制格式而不是文本格式存储或读取。它比文本和 CSV 格式稍快一些,但二进制格式文件在不同的机器架构和 PostgreSQL 版本之间的可移植性较差。此外,二进制格式与数据类型高度相关。例如,不能从
smallint 列输出二进制数据再读入到 integer 列中,尽管这种做法在文本格式下是可行的。
binary 文件格式由文件头、零个或多个包含行数据的元组以及一个文件尾构成。头部和数据都以网络字节序表示。
注意
7.4 之前的 PostgreSQL 版本使用一种不同的二进制文件格式。
文件头
文件头由 15 字节的固定字段构成,后面跟着一个变长的头部扩展区。固定字段有:
- 签名
11 字节序列
PGCOPY\n\377\r\n\0— 注意,零字节是签名中必不可少的一部分。(该签名的设计目的是便于识别那些在不具备 8 位透明性的传输过程中遭到破坏的文件。行尾转换过滤器、零字节丢失、高位丢失或奇偶校验变化等情况都会改变该签名。)- 标志字段
这是一个 32 位整数位掩码,用于表示文件格式的重要属性。位编号从 0(LSB)到 31(MSB)。注意,此字段与文件格式中的所有整数字段一样,采用网络字节序存储(最高有效字节在前)。位 16-31 保留用于表示文件格式的关键问题;如果读取程序发现这个范围内有非预期的位被置位,就应中止。位 0-15 保留用于表示向后兼容的格式问题;读取程序应直接忽略这个范围内非预期的置位。目前仅定义了一个标志位,其余位必须为零:
- 位 16
如果为 1,则数据中包含 OID;如果为 0,则不包含。
- 头部扩展区长度
32 位整数,表示头部剩余部分的长度(以字节计),不包括该字段本身。当前该值为零,因此其后紧接着第一个元组。未来对这种格式的更改可能允许在头部中包含额外数据。如果读取程序不知道如何处理头部扩展区数据,应静默跳过它。
头部扩展区被设想为包含一系列可自我标识的块。标志字段并不用于告诉读取程序扩展区中包含哪些内容。头部扩展内容的具体设计留待后续版本决定。
这种设计既允许向后兼容的头部新增(增加头部扩展块,或设置低位标志位),也允许不向后兼容的更改(设置高位标志位来表明这类更改,并在需要时向扩展区增加支持数据)。
元组
每个元组都以一个 16 位整数计数开头,用于表示该元组中的字段数。(目前,一个表中的所有元组都应有相同的计数,但这未必永远如此。)随后,对元组中的每个字段,都会有一个 32 位长度字,后跟相应字节数的字段数据。(长度字不包括其本身,且可以为零。)特殊情况下,-1 表示一个 NULL 字段值;在 NULL 情况下,后面不会跟随任何值字节。
字段之间没有对齐填充或任何其他额外数据。
当前,二进制格式文件中的所有数据值都假定为二进制格式(格式代码一)。可以预见,未来的扩展可能会增加一个允许为各列分别指定格式代码的头部字段。
要确定实际元组数据应采用的二进制格式,你应该参考
PostgreSQL 源码,特别是各列数据类型对应的*send 和*recv 函数(这些函数通常可以在源码分发包的 src/backend/utils/adt/目录中找到)。
如果文件中包含 OID,则 OID 字段紧跟在字段计数值之后。它是一个普通字段,只是不计入字段数。特别是,它带有一个长度字段 — 这样就能较容易地处理 4 字节或 8 字节的 OID,也允许在将来确有需要时把 OID 表示为空值。
文件尾
文件尾由一个值为 -1 的 16 位整数构成。这很容易与元组的字段计数字区分开来。
如果字段计数字既不是 -1 也不是预期的列数,读取程序应报告错误。这提供了一项额外检查,以防与数据失去同步。
示例
下面的示例使用竖线(|)作为字段分隔符将一个表复制到客户端:
COPY country TO STDOUT (DELIMITER '|');
要将文件中的数据复制到 country 表中:
COPY country FROM '/usr1/proj/bray/sql/country_data';
只把名称以 'A' 开头的国家复制到一个文件中:
COPY (SELECT * FROM country WHERE country_name LIKE 'A%') TO '/usr1/proj/bray/sql/a_list_countries.copy';
要复制到压缩文件中,可以将输出通过管道送入外部压缩程序:
COPY country TO PROGRAM 'gzip > /usr1/proj/bray/sql/country_data.gz';
下面给出适合从 STDIN 复制到表中的示例数据:
AF AFGHANISTAN AL ALBANIA DZ ALGERIA ZM ZAMBIA ZW ZIMBABWE
注意每一行中的空白实际上是一个制表符。
下面是用二进制格式输出的相同数据。该数据是用 Unix 工具
od -c 过滤后显示的。该表具有三列,第一列类型是 char(2),第二列类型是 text,第三列类型是 integer。所有行在第三列都是空值。
0000000 P G C O P Y \n 377 \r \n \0 \0 \0 \0 \0 \0 0000020 \0 \0 \0 \0 003 \0 \0 \0 002 A F \0 \0 \0 013 A 0000040 F G H A N I S T A N 377 377 377 377 \0 003 0000060 \0 \0 \0 002 A L \0 \0 \0 007 A L B A N I 0000100 A 377 377 377 377 \0 003 \0 \0 \0 002 D Z \0 \0 \0 0000120 007 A L G E R I A 377 377 377 377 \0 003 \0 \0 0000140 \0 002 Z M \0 \0 \0 006 Z A M B I A 377 377 0000160 377 377 \0 003 \0 \0 \0 002 Z W \0 \0 \0 \b Z I 0000200 M B A B W E 377 377 377 377 377 377
兼容性
SQL 标准中没有 COPY 语句。
下列语法在 PostgreSQL 9.0 之前的版本中使用,现仍受支持:
COPYtable_name[ (column_name[, ...] ) ] FROM { 'filename' | STDIN } [ [ WITH ] [ BINARY ] [ OIDS ] [ DELIMITER [ AS ] 'delimiter_character' ] [ NULL [ AS ] 'null string' ] [ CSV [ HEADER ] [ QUOTE [ AS ] 'quote_character' ] [ ESCAPE [ AS ] 'escape_character' ] [ FORCE NOT NULLcolumn_name[, ...] ] ] ] COPY {table_name[ (column_name[, ...] ) ] | (query) } TO { 'filename' | STDOUT } [ [ WITH ] [ BINARY ] [ OIDS ] [ DELIMITER [ AS ] 'delimiter_character' ] [ NULL [ AS ] 'null string' ] [ CSV [ HEADER ] [ QUOTE [ AS ] 'quote_character' ] [ ESCAPE [ AS ] 'escape_character' ] [ FORCE QUOTE {column_name[, ...] | * } ] ] ]
注意在这种语法中,BINARY 和 CSV 被视为独立的关键字,而不是 FORMAT 选项的参数。
下列语法在 PostgreSQL 7.3 之前的版本中使用,现仍受支持:
COPY [ BINARY ]table_name[ WITH OIDS ] FROM { 'filename' | STDIN } [ [USING] DELIMITERS 'delimiter_character' ] [ WITH NULL AS 'null_string' ] COPY [ BINARY ]table_name[ WITH OIDS ] TO { 'filename' | STDOUT } [ [USING] DELIMITERS 'delimiter_character' ] [ WITH NULL AS 'null_string' ]