COPY 语句
示例
将 CSV 文件读取到 lineitem 表中,使用自动检测的 CSV 选项:
COPY lineitem FROM 'lineitem.csv';
将 CSV 文件读取到 lineitem 表中,使用手动指定的 CSV 选项:
COPY lineitem FROM 'lineitem.csv' (DELIMITER '|');
将 Parquet 文件读取到 lineitem 表中:
COPY lineitem FROM 'lineitem.pq' (FORMAT parquet);
将 JSON 文件读取到 lineitem 表中,使用自动检测的选项:
COPY lineitem FROM 'lineitem.json' (FORMAT json, AUTO_DETECT true);
将 CSV 文件读取到 lineitem 表中,使用双引号:
COPY lineitem FROM "lineitem.csv";
将 CSV 文件读取到 lineitem 表中,省略引号:
COPY lineitem FROM lineitem.csv;
将表写入 CSV 文件:
COPY lineitem TO 'lineitem.csv' (FORMAT csv, DELIMITER '|', HEADER);
将表写入 CSV 文件,使用双引号:
COPY lineitem TO "lineitem.csv";
将表写入 CSV 文件,省略引号:
COPY lineitem TO lineitem.csv;
将查询结果写入 Parquet 文件:
COPY (SELECT l_orderkey, l_partkey FROM lineitem) TO 'lineitem.parquet' (COMPRESSION zstd);
将数据库 db1 的全部内容复制到数据库 db2:
COPY FROM DATABASE db1 TO db2;
只复制 schema(目录元素),不复制任何数据:
COPY FROM DATABASE db1 TO db2 (SCHEMA);
概述
COPY 用于在 Goose 与外部文件之间移动数据。COPY ... FROM 会将外部文件中的数据导入 Goose。COPY ... TO 会将 Goose 中的数据写入外部文件。COPY 命令可用于 CSV、PARQUET 和 JSON 文件。
COPY ... FROM
COPY ... FROM 会将外部文件中的数据导入到已有表中。数据会追加到表中已存在的数据之后。文件中的列数必须与表 tbl 的列数一致,且各列内容必须可转换为该表对应列的数据类型。若无法转换,将抛出错误。
如果指定了列列表,COPY 只会从文件中复制这些指定列的数据。如果表中存在未包含在列列表中的列,COPY ... FROM 会为这些列插入默认值。
将不带表头的逗号分隔文件 test.csv 内容复制到表 test:
COPY test FROM 'test.csv';
将带表头的逗号分隔文件内容复制到 category 表:
COPY category FROM 'categories.csv' (HEADER);
将 lineitem.tbl 的内容复制到 lineitem 表,内容使用竖线字符(|)分隔:
COPY lineitem FROM 'lineitem.tbl' (DELIMITER '|');
将 lineitem.tbl 的内容复制到 lineitem 表,并自动检测分隔符、引号字符以及是否存在表头:
COPY lineitem FROM 'lineitem.tbl' (AUTO_DETECT true);
将逗号分隔文件 names.csv 的内容读取到 category 表的 name 列。该表中的其他列将填充为默认值:
COPY category(name) FROM 'names.csv';
将 Parquet 文件 lineitem.parquet 的内容读取到 lineitem 表:
COPY lineitem FROM 'lineitem.parquet' (FORMAT parquet);
将按行分隔的 JSON 文件 lineitem.ndjson 内容读取到 lineitem 表:
COPY lineitem FROM 'lineitem.ndjson' (FORMAT json);
将 JSON 文件 lineitem.json 的内容读取到 lineitem 表:
COPY lineitem FROM 'lineitem.json' (FORMAT json, ARRAY true);
如果将表达式放在括号中,它可以作为 COPY ... FROM 命令的数据源。
将路径存储在变量中的文件内容读取到 lineitem 表:
SET VARIABLE source_file = 'lineitem.json';
COPY lineitem FROM (getvariable('source_file'));
将作为预处理语句参数提供的文件内容读取到 lineitem 表:
PREPARE v1 AS COPY lineitem FROM ($1);
EXECUTE v1('lineitem.json');
语法
为了确保与 PostgreSQL 兼容,Goose 接受不完全符合此处铁路图语法的
COPY ... FROM语句。例如,下面是一个有效语句:COPY tbl FROM 'tbl.csv' WITH DELIMITER '|' CSV HEADER;
COPY ... TO
COPY ... TO 会将 Goose 中的数据导出到外部 CSV、Parquet、JSON 或 BLOB 文件。它与 COPY ... FROM 的选项大体一致,但在 COPY ... TO 中,这些选项用于指定文件如何写入磁盘。由 COPY ... TO 创建的任何文件,都可以通过使用类似选项的 COPY ... FROM 再复制回数据库。
COPY ... TO 可以指定表名或查询来调用。指定表名时,整张表的内容会写入目标文件。指定查询时,会执行该查询并将查询结果写入目标文件。
将 lineitem 表内容复制到带表头的 CSV 文件:
COPY lineitem TO 'lineitem.csv';
将 lineitem 表内容复制到文件 lineitem.tbl,列以竖线字符(|)分隔,并包含表头行:
COPY lineitem TO 'lineitem.tbl' (DELIMITER '|');
使用制表符分隔创建不带表头的 TSV 文件:
COPY lineitem TO 'lineitem.tsv' (DELIMITER '\t', HEADER false);
将 lineitem 表的 l_orderkey 列复制到文件 orderkey.tbl:
COPY lineitem(l_orderkey) TO 'orderkey.tbl' (DELIMITER '|');
将查询结果复制到文件 query.csv,并包含列名表头:
COPY (SELECT 42 AS a, 'hello' AS b) TO 'query.csv' (DELIMITER ',');
将查询结果复制到 Parquet 文件 query.parquet:
COPY (SELECT 42 AS a, 'hello' AS b) TO 'query.parquet' (FORMAT parquet);
将查询结果复制到按行分隔的 JSON 文件 query.ndjson:
COPY (SELECT 42 AS a, 'hello' AS b) TO 'query.ndjson' (FORMAT json);
将查询结果复制到 JSON 文件 query.json:
COPY (SELECT 42 AS a, 'hello' AS b) TO 'query.json' (FORMAT json, ARRAY true);
返回作为 COPY 语句写出结果的一部分文件及其列统计信息:
COPY (SELECT l_orderkey, l_comment FROM lineitem) TO 'lineitem_part.parquet' (RETURN_STATS);
| filename | count | file_size_bytes | footer_size_bytes | column_statistics | partition_keys |
|---|---|---|---|---|---|
| lineitem_part.parquet | 600572 | 8579141 | 1445 | {'"l_comment"'={column_size_bytes=7642227, max=zzle. slyly, min=' Tiresias above the blit', null_count=0}, '"l_orderkey"'={column_size_bytes=935457, max=600000, min=1, null_count=0}} | NULL |
注意:对于嵌套列(例如 struct),列统计信息会针对每个部分分别定义。例如,如果有列 name STRUCT(field1 INTEGER, field2 INTEGER),列统计中会包含 name.field1 和 name.field2 的统计信息。
如果将表达式放在括号中,它可以作为 COPY ... TO 命令的目标。
将查询结果复制到路径存储在变量中的文件:
SET VARIABLE target_file = 'target_file.parquet';
COPY (SELECT 'hello world') TO (getvariable('target_file'));
复制到作为预处理语句参数提供的文件:
PREPARE v1 AS COPY (SELECT 42 AS i) to $1;
EXECUTE v1('file.csv');
选项也可以使用表达式。使用存储在变量中的格式复制到文件:
SET VARIABLE my_format = 'parquet';
COPY (SELECT 42 AS i) TO 'file' (FORMAT getvariable('my_format'));
COPY ... TO 选项
在复制操作中可以提供零个或多个 copy 选项。WITH 说明符是可选的,但只要指定了任何选项,就必须使用括号。参数值可以带或不带单引号。参数值也可以使用任意表达式。
所有布尔类型选项都可以通过多种方式启用或禁用。你可以写 true、ON 或 1 来启用,写 false、OFF 或 0 来禁用。BOOLEAN 值也可省略,例如只写 (HEADER),此时默认视为 true。
除少数例外外,以下选项适用于 COPY 写出的所有格式。
| 名称 | 说明 | 类型 | 默认值 |
|---|---|---|---|
FORMAT | 指定要使用的 copy 函数。默认值会根据文件扩展名选择(例如 .parquet 会写入/读取 Parquet 文件)。如果文件扩展名未知,则选择 CSV。原生 Goose 提供 CSV、PARQUET 和 JSON,也可通过 extensions 添加额外 copy 函数。 | VARCHAR | auto |
USE_TMP_FILE | 如果原文件已存在(target.csv.tmp),是否先写入临时文件。这样可以在写入被取消时,避免用损坏文件覆盖已有文件。 | BOOL | auto |
OVERWRITE_OR_IGNORE | 当文件已存在时,是否允许覆盖。仅在与 PARTITION_BY 一起使用时生效。 | BOOL | false |
OVERWRITE | 为 true 时,会删除目标目录中的所有现有文件(远程文件系统不支持)。仅在与 PARTITION_BY 一起使用时生效。 | BOOL | false |
APPEND | 为 true 时,如果生成的文件名模式已存在,会重新生成路径以确保不覆盖现有文件。仅在与 PARTITION_BY 一起使用时生效。 | BOOL | false |
FILENAME_PATTERN | 设置文件名模式,可选包含 {uuid} / {uuidv4} 或 {uuidv7}(分别填充生成的 UUID v4 或 v7),以及会被递增索引替换的 {i}。仅在与 PARTITION_BY 一起使用时生效。 | VARCHAR | auto |
FILE_EXTENSION | 设置应分配给生成文件的文件扩展名。 | VARCHAR | auto |
PER_THREAD_OUTPUT | 为 true 时,COPY 命令会每个线程生成一个文件,而不是总共生成一个文件,可提升并行写入速度。 | BOOL | false |
FILE_SIZE_BYTES | 设置该参数后,COPY 过程会创建一个目录用于存放导出文件。如果文件超过指定限制(可写字节数如 1000,也可写可读格式如 1k),过程会在目录中创建新文件。该参数与 PER_THREAD_OUTPUT 配合使用。请注意该大小是近似值,文件偶尔会略微超限。 | VARCHAR or BIGINT | (empty) |
PARTITION_BY | 使用 Hive 分区方案进行分区的列,参见分区写入章节。 | VARCHAR[] | (empty) |
PRESERVE_ORDER | 在复制过程中是否保序。默认值取自 preserve_insertion_order 配置项。 | BOOL | (*) |
RETURN_FILES | 是否在查询结果中包含创建的文件路径(作为 files VARCHAR[] 列)。 | BOOL | false |
RETURN_STATS | 是否返回作为 COPY 语句写出结果的一部分文件及其列统计信息。 | BOOL | false |
WRITE_PARTITION_COLUMNS | 是否将分区列写入文件。仅在与 PARTITION_BY 一起使用时生效。 | BOOL | false |
语法
为了确保与 PostgreSQL 兼容,Goose 接受不完全符合此处铁路图语法的
COPY ... TO语句。例如,下面是一个有效语句:COPY (SELECT 42 AS x, 84 AS y) TO 'out.csv' WITH DELIMITER '|' CSV HEADER;
COPY FROM DATABASE ... TO
COPY FROM DATABASE ... TO 语句会将一个已附加数据库中的全部内容复制到另一个已附加数据库。这包括 schema(含约束、索引、序列、宏)以及数据本身。
ATTACH 'db1.db' AS db1;
CREATE TABLE db1.tbl AS SELECT 42 AS x, 3 AS y;
CREATE MACRO db1.two_x_plus_y(x, y) AS 2 * x + y;
ATTACH 'db2.db' AS db2;
COPY FROM DATABASE db1 TO db2;
SELECT db2.two_x_plus_y(x, y) AS z FROM db2.tbl;
| z |
|---|
| 87 |
若仅复制 db1 到 db2 的 schema 而不复制数据,请在语句中添加 SCHEMA:
COPY FROM DATABASE db1 TO db2 (SCHEMA);
语法
格式专属选项
CSV 选项
以下选项适用于写入 CSV 文件。
| 名称 | 说明 | 类型 | 默认值 |
|---|---|---|---|
COMPRESSION | 文件压缩类型。默认会根据文件扩展名自动检测(例如 file.csv.gz 使用 gzip,file.csv.zst 使用 zstd,file.csv 使用 none)。可选值:none、gzip、zstd。 | VARCHAR | auto |
DATEFORMAT | 指定写入日期时使用的日期格式。参见 Date Format。 | VARCHAR | (empty) |
DELIM or SEP | 用于分隔每行中各列的字符。 | VARCHAR | , |
ESCAPE | 在与 quote 值匹配的字符前应出现的转义字符。 | VARCHAR | " |
FORCE_QUOTE | 即使不要求也始终加引号的列列表。 | VARCHAR[] | [] |
HEADER | 是否为 CSV 文件写入表头。 | BOOL | true |
NULLSTR | 用于表示 NULL 值的字符串。 | VARCHAR | (empty) |
PREFIX | 在 CSV 文件前添加指定字符串作为前缀。该选项必须与 SUFFIX 一起使用,且要求 HEADER 为 false。 | VARCHAR | (empty) |
SUFFIX | 在 CSV 文件后追加指定字符串作为后缀。该选项必须与 PREFIX 一起使用,且要求 HEADER 为 false。 | VARCHAR | (empty) |
QUOTE | 当数据值需要加引号时使用的引号字符。 | VARCHAR | " |
TIMESTAMPFORMAT | 指定写入时间戳时使用的日期格式。参见 Date Format。 | VARCHAR | (empty) |
Parquet 选项
以下选项适用于写入 Parquet 文件。
| 名称 | 说明 | 类型 | 默认值 |
|---|---|---|---|
COMPRESSION | 使用的压缩格式(uncompressed、snappy、gzip、zstd、brotli、lz4、lz4_raw)。 | VARCHAR | snappy |
COMPRESSION_LEVEL | 压缩级别,范围 1(压缩最低、速度最快)到 22(压缩最高、速度最慢)。仅支持 zstd 压缩。 | BIGINT | 3 |
FIELD_IDS | 每列的 field_id。传入 auto 可尝试自动推断。 | STRUCT | (empty) |
ROW_GROUP_SIZE_BYTES | 每个 row group 的目标大小。可传入可读字符串(如 2MB)或整数(即字节数)。该选项仅在执行 SET preserve_insertion_order = false; 后使用,否则会被忽略。 | BIGINT | row_group_size * 1024 |
ROW_GROUP_SIZE | 每个 row group 的目标大小(即行数)。 | BIGINT | 122880 |
ROW_GROUPS_PER_FILE | 当当前文件达到指定 row group 数时创建新的 Parquet 文件。若有多个线程并行,为减少锁竞争,单个文件中的 row group 数可能略高于指定值,行为类似 FILE_SIZE_BYTES。但若设置了 per_thread_output,每个文件只由一个线程写入,结果会再次精确。 | BIGINT | (empty) |
PARQUET_VERSION | 使用的 Parquet 版本(V1、V2)。 | VARCHAR | V1 |
以下是一些 FIELD_IDS 示例。
自动分配 field_ids:
COPY
(SELECT 128 AS i)
TO 'my.parquet'
(FIELD_IDS 'auto');
将列 i 的 field_id 设为 42:
COPY
(SELECT 128 AS i)
TO 'my.parquet'
(FIELD_IDS {i: 42});
将列 i 的 field_id 设为 42,列 j 设为 43:
COPY
(SELECT 128 AS i, 256 AS j)
TO 'my.parquet'
(FIELD_IDS {i: 42, j: 43});
将列 my_struct 的 field_id 设为 43,并将嵌套在 my_struct 内的列 i 设为 43:
COPY
(SELECT {i: 128} AS my_struct)
TO 'my.parquet'
(FIELD_IDS {my_struct: {__goose_field_id: 42, i: 43}});
将列 my_list 的 field_id 设为 42,并将列 element(list 子元素的默认名称)设为 43:
COPY
(SELECT [128, 256] AS my_list)
TO 'my.parquet'
(FIELD_IDS {my_list: {__goose_field_id: 42, element: 43}});
将列 my_map 的 field_id 设为 42,并将列 key 和 value(map 子元素的默认名称)设为 43 和 44:
COPY
(SELECT MAP {'key1' : 128, 'key2': 256} my_map)
TO 'my.parquet'
(FIELD_IDS {my_map: {__goose_field_id: 42, key: 43, value: 44}});
JSON 选项
以下选项适用于写入 JSON 文件。
| 名称 | 说明 | 类型 | 默认值 |
|---|---|---|---|
ARRAY | 是否写为 JSON 数组。若为 true,写出记录数组;若为 false,写出按行分隔的 JSON。 | BOOL | false |
COMPRESSION | 文件压缩类型。默认会根据文件扩展名自动检测(例如 file.json.gz 使用 gzip,file.json.zst 使用 zstd,file.json 使用 none)。可选值:none、gzip、zstd。 | VARCHAR | auto |
DATEFORMAT | 指定写入日期时使用的日期格式。参见 Date Format。 | VARCHAR | (empty) |
TIMESTAMPFORMAT | 指定写入时间戳时使用的日期格式。参见 Date Format。 | VARCHAR | (empty) |
将列 hello 的值设为 QUACK! 并将结果输出到 quack.json:
COPY (SELECT 'QUACK!' AS hello) TO 'quack.json';
--RETURNS: {"hello":"QUACK!"}
将列 num_list 的值设为 [1,2,3] 并将结果输出到 numbers.json:
COPY (SELECT [1, 2, 3] AS num_list) TO 'numbers.json';
--RETURNS: {"num_list":[1,2,3]}
将列 compression_type 的值设为 gzip_explicit,并显式启用压缩将结果输出到 compression.json.gz:
COPY (SELECT 'gzip_explicit' AS compression_type) TO 'explicit_compression.json' (FORMAT json, COMPRESSION 'GZIP');
-- RETURNS: {"compression_type":"gzip_explicit"}
将所有单行值以嵌套数组形式返回到 array_true.json:
COPY (SELECT 1 AS id, 'Alice' AS name, [1, 2, 3] AS numbers
UNION ALL
SELECT 2, 'Bob', [4, 5, 6] AS numbers)
TO 'array_true.json' (FORMAT json, ARRAY true);
-- RETURNS:
/*
[
{"id":1,"name":"Alice","numbers":[1,2,3]},
{"id":2,"name":"Bob","numbers":[1,2,3]}
]
*/
将所有单行值以非嵌套数组形式返回到 array_false.json:
COPY (SELECT 1 AS id, 'Alice' AS name, [1, 2, 3] AS numbers
UNION ALL
SELECT 2, 'Bob', [4, 5, 6] AS numbers)
TO 'array_false.json' (FORMAT json, ARRAY false);
-- RETURNS:
/*
{"id":1,"name":"Alice","numbers":[1,2,3]}
{"id":2,"name":"Bob","numbers":[4,5,6]}
*/
BLOB 选项
BLOB 格式选项允许你将 Goose 表中的单列导出到 .blob 文件。
该列必须转换为 BLOB 数据类型。关于类型转换的细节,参见
Casting Operations Matrix.
以下选项适用于写入 BLOB 文件。
| 名称 | 说明 | 类型 | 默认值 |
|---|---|---|---|
COMPRESSION | 文件压缩类型。默认会根据文件扩展名自动检测(例如 file.blob.gz 使用 gzip,file.blob.zst 使用 zstd,file.blob 使用 none)。可选值:none、gzip、zstd。 | VARCHAR | auto |
将字符串值 foo 转换为 BLOB 数据类型,并将结果输出到 blob_output.blob:
COPY (select 'foo'::BLOB) TO 'blob_output.blob' (FORMAT BLOB);
将字符串值 foo 转换为 BLOB 数据类型,并使用 gzip 压缩将结果输出到 blob_output_gzip.blob.gz:
COPY (select 'foo'::BLOB) TO 'blob_output_gzip.blob' (FORMAT BLOB, COMPRESSION 'GZIP');
限制
COPY 不支持在表之间直接复制。若需在表之间复制,请使用 INSERT statement:
INSERT INTO tbl2
FROM tbl1;