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用户执行。只有数据库超级用户才允许使用指定文件名或命令的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)文件格式。不同于 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 保留用于表示向后兼容的格式问题;读取程序应直接忽略这个范围内非预期的置位。目前仅定义了一个标志位,其余位必须为零:
如果为 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语句。
以下语法曾在PostgreSQL9.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选项的参数。
以下语法曾在PostgreSQL7.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' ]