

 从补丁 198 开始，Amazon Redshift 将不再支持创建新的 Python UDF。现有的 Python UDF 将继续正常运行至 2026 年 6 月 30 日。有关更多信息，请参阅[博客文章](https://aws.amazon.com/blogs/big-data/amazon-redshift-python-user-defined-functions-will-reach-end-of-support-after-june-30-2026/)。

# SQL 命令
<a name="iceberg-writes-sql-syntax"></a>

Amazon Redshift 中的 Apache Iceberg 表提供了一种强大的方法用于管理数据湖中的大型分析数据集。这些表支持 ACID 事务、架构发展和时间旅行功能，同时保持了高性能的分析工作负载。使用 Apache Iceberg 表，您可以高效地组织和分区数据，控制文件格式和文件压缩，并与其他 AWS 服务无缝集成。

您可以使用 `CREATE TABLE ... USING ICEBERG` 和 `CREATE TABLE ... USING ICEBERG AS SELECT` 命令创建分区和未分区的 Iceberg 表。您可以使用外部架构表示法 (`external_schema.table_name`) 或三部分表示法 (`"catalog_name".database_name.table_name`) 引用 Iceberg 表。本部分中的示例将演示这两种方法。

创建表之后，您可以使用标准 `INSERT` 命令添加数据。请记住，虽然 Amazon Redshift 可处理许多 Iceberg 数据类型，但在插入信息时，您可能需要转换某些数据格式。

您可以使用 `SHOW TABLES` 命令查看 Iceberg 表。如果要从 AWS Glue Data Catalog 中移除表，您可以使用 `DROP TABLE` 命令。请注意，这只会移除表注册信息。实际数据仍保留在存储中，直到您单独将其删除。

您也可以使用 `DELETE`、`UPDATE` 和 `MERGE` 命令修改现有数据。Iceberg 表目前尚不支持所有其他 SQL 语句，例如 `ALTER TABLE`。

您可以对并非由 Amazon Redshift 创建的 Iceberg 表执行写入操作。但是，这里有一些限制：
+ 该表必须是 Iceberg v2 表。
+ 该表必须使用 Parquet 作为默认数据格式。
+ 该表不可将元数据压缩设置为 True。
+ 该表不可启用写入-审计-发布（WAP）。

以下部分演示了在 Amazon Redshift 中创建、插入、修改和管理 Iceberg 表的 SQL 语法。

**Contents**
+ [CREATE TABLE](#iceberg-writes-create-table)
+ [CREATE TABLE AS SELECT](#iceberg-writes-create-table-as-select)
+ [SHOW TABLE](#iceberg-writes-show-table)
+ [INSERT INTO](#iceberg-writes-insert-into)
+ [DELETE](#iceberg-writes-delete)
+ [UPDATE](#iceberg-writes-update)
+ [MERGE](#iceberg-writes-merge)
+ [DROP TABLE](#iceberg-writes-drop-table)

## CREATE TABLE
<a name="iceberg-writes-create-table"></a>

```
CREATE TABLE [IF NOT EXISTS] {{<external_schema>}}.{{<table_name>}} (
  column_name data_type [, ...]
)
USING ICEBERG
[LOCATION 's3://{{your-bucket-name}}/prefix/']
[PARTITIONED BY [[column_name | transform_function]], ...]
[TABLE PROPERTIES ('compression_type'='<compression_value>')]
```

对于 S3 表存储桶，您也可以使用三部分表示法。

```
CREATE TABLE "{{<table_bucket_name>}}@s3tablescatalog".{{<database_name>}}.{{<table_name>}} (
  column_name data_type [, ...]
)
USING ICEBERG
[PARTITIONED BY [[column_name | transform_function]], ...]
[TABLE PROPERTIES ('compression_type'='<compression_value>')]
```

请注意，`{{<external_schema>}}` 必须是创建外部表时使用的现有外部架构名称。有关如何创建和管理外部架构的更多信息，请参阅 Amazon Redshift 文档中的 [CREATE EXTERNAL SCHEMA](https://docs.aws.amazon.com/redshift/latest/dg/r_CREATE_EXTERNAL_SCHEMA.html)。

`LOCATION` 子句定义此新创建的 Iceberg 表的表位置。对于 Amazon S3 表，无法指定 `LOCATION`，因为表的位置由 Amazon S3 表目录 (`s3tablescatalog`) 确定。

在所有其他情况下，`LOCATION` 均必填，并且应为空位置，也就是说没有现有的 Amazon S3 对象共享了这个相同的存储桶和前缀。请注意，Amazon S3 存储桶必须与 Amazon Redshift 集群位于同一区域。

但是，AWS 提供了一种方法，用于将存储在一个 AWS 区域的 AWS Glue Data Catalog 中的 Iceberg 表的数据复制到不同 AWS 区域，这样您就可以将写入操作复制到不同的区域。有关更多信息，请参阅[跨 AWS 区域复制数据](https://docs.aws.amazon.com/prescriptive-guidance/latest/apache-iceberg-on-aws/best-practices-workloads.html#workloads-replication)。

`PARTITIONED BY` 定义 Iceberg 表分区。Amazon Redshift 支持所有 Iceberg v2 分区转换，但 `void` 除外。以下是当前支持的转换列表：
+ **身份**
+ **bucket[N]**
+ **truncate[W]**
+ **年**
+ **个月**
+ **day**
+ **小时**

有关这些转换的完整定义以及兼容的数据类型，请参阅 Apache Iceberg 文档中的 [Partition Transforms](https://iceberg.apache.org/spec/#partition-transforms)。

`PARTITIONED BY` 支持多个级别的分区。例如，您可以运行以下命令：

```
CREATE TABLE ...
USING ICEBERG
LOCATION ...
PARTITIONED BY (bucket(16, id), year(ship_date));
```

但是，Amazon Redshift 不支持在多个转换中使用单列。例如，不支持以下语法。

```
CREATE TABLE ...
USING ICEBERG
LOCATION ...
PARTITIONED BY (bucket(16, ship_date), year(ship_date));
```

`TABLE PROPERTIES` 子句定义此 Iceberg 表的额外表属性。唯一支持的表属性是 `compression_type`，该属性定义默认的 Parquet 数据文件压缩。如果未指定该项，则使用 `snappy` 作为压缩编解码器。`compression_type` 的可能值包括 `zstd`、`brotli`、`gzip`、`snappy` 和 `uncompressed`。

**注意**  
Iceberg 表不支持 `CREATE TABLE ... LIKE ...`。此外，Iceberg 表不支持列约束和列属性，这与 RMS 表不同。

或者，您可以使用 `CREATE TABLE AS SELECT` 在单个操作中创建和填充 Iceberg 表：

## CREATE TABLE AS SELECT
<a name="iceberg-writes-create-table-as-select"></a>

```
CREATE TABLE {{<external_schema>}}.{{<table_name>}} [(
  column_name[, ...]
)]
USING ICEBERG
[LOCATION 's3://{{your-bucket-name}}/prefix/']
[PARTITIONED BY [[column_name | transform_function]], ...]
[TABLE PROPERTIES ('compression_type'='{{<compression-value>}}')]
AS
SELECT query
```

您也可以使用三部分表示法在自动挂载目录中创建表：

```
CREATE TABLE "{{<catalog_name>}}".{{<database_name>}}.{{<table_name>}} [(
  column_name[, ...]
)]
USING ICEBERG
[LOCATION 's3://{{your-bucket-name}}/prefix/']
[PARTITIONED BY [[column_name | transform_function]], ...]
[TABLE PROPERTIES ('compression_type'='{{<compression-value>}}')]
AS
SELECT query
```

这与 `CREATE TABLE` 语句类似，不同之处在于 `CREATE` 后跟 `SELECT` 语句，用于在表中填充 `SELECT` 查询结果。

此处的 `CREATE TABLE` 子句不再允许您指定数据类型，因为列的数据类型将由 `SELECT` 查询决定。

如果 `SELECT` 查询因任何原因失败，则此查询将失败，并且不会创建 Iceberg 表。

您可以使用 `SHOW TABLE` 查看 Iceberg 表的结构：

## SHOW TABLE
<a name="iceberg-writes-show-table"></a>

```
SHOW TABLE {{<external_schema>}}.{{<table_name>}}
```

您还可以对自动挂载目录使用三部分表示法：

```
SHOW TABLE "{{<catalog_name>}}".{{<database_name>}}.{{<table_name>}}
```

`SHOW TABLE` 显示 Iceberg 表的 `CREATE TABLE` 语句。该命令将根据表的类型显示相应的结果。以下是 Iceberg 表的 `SHOW TABLE` 输出示例：

```
CREATE TABLE my_schema.items (id int, price decimal(5, 2))
USING ICEBERG
LOCATION 's3://my_s3_bucket/items/'
PARTITIONED BY (bucket(16, id))
TABLE PROPERTIES ('compression_type'='snappy')
```

**注意**  
对于 Amazon S3 表，由于表位置由 Amazon S3 表目录管理，因此 `SHOW TABLE` 结果中将省略 `LOCATION` 子句。

创建表后，您可以使用 `INSERT INTO` 添加数据：

## INSERT INTO
<a name="iceberg-writes-insert-into"></a>

```
INSERT INTO {{<external_schema>}}.{{<table_name>}} [(column_name [, ...])] VALUES (...)
INSERT INTO {{<external_schema>}}.{{<table_name>}} [(column_name [, ...])] (SELECT query)

-- Using three-part notation for S3 table buckets:
INSERT INTO "{{<table_bucket_name>}}@s3tablescatalog".{{<database_name>}}.{{<table_name>}} [(column_name [, ...])] VALUES (...)
INSERT INTO "{{<table_bucket_name>}}@s3tablescatalog".{{<database_name>}}.{{<table_name>}} [(column_name [, ...])] (SELECT query)
```

您可以使用上述语法对现有 Iceberg 表执行 `INSERT INTO`。如果使用 `VALUES` 子句，则按 `column_name` 列出您为列提供的值，如果省略了 `column_name` 部分，则提供所有列的值。

将数据插入分区表时，将根据预定义的分区规范分布新行。如果 `SELECT` 查询由于任何原因失败，则查询将失败，并且不向 Iceberg 表中插入任何数据。

## DELETE
<a name="iceberg-writes-delete"></a>

Iceberg 表的 `DELETE` 查询使用 RMS 表中的现有 `DELETE` 语法：

```
[ WITH [RECURSIVE] {{common_table_expression}} [, {{common_table_expression}} , ...] ]
DELETE [ FROM ] {{iceberg_table}}
[ { USING } {{table_name, ...}} ] [ WHERE {{condition}} ]
```

对于 S3 表存储桶，您也可以使用三部分表示法。

```
[ WITH [RECURSIVE] {{common_table_expression}} [, {{common_table_expression}} , ...] ]
DELETE [ FROM ] "{{<table_bucket_name>}}@s3tablescatalog".{{<database_name>}}.{{<table_name>}}
[ { USING } {{table_name, ...}} ] [ WHERE {{condition}} ]
```

`{{iceberg_table}}` 可以使用 `{{<external_schema>}}.{{<external_table_name>}}` 格式进行引用，也可以使用三部分表示法来表示自动挂载目录。请参阅[在 Amazon Redshift 中引用 Iceberg 表](https://docs.aws.amazon.com/redshift/latest/dg/referencing-iceberg-tables.html)。

`USING` 子句中的 `{{table_name}}` 可用来联接到目标表，以删除满足 `WHERE` 条件的行。`{{table_name}}` 可以是 Iceberg 表，也可以是 Amazon Redshift RMS 表。

由于 Iceberg 使用隐藏分区架构，因此用户可以使用 `DELETE` 查询来删除分区，实现与 Hive 表上的 `ALTER TABLE ... DROP PARTITION ...` 相同的效果。

例如，我们有如下所示的分区 Iceberg 表：

```
CREATE TABLE my_external_schema.lineitem
(l_item_id int,
 l_ship_date varchar,
 ...
)
USING ICEBERG
LOCATION ...
PARTITIONED BY l_ship_date;
```

然后我们可以使用如下查询轻松删除分区：

```
DELETE FROM my_external_schema.lineitem WHERE l_ship_date = '20251231';
```

对于这样的查询，Amazon Redshift 将优化执行，使其仅执行元数据操作并缩短执行循环时间。因此，与普通 `DELETE` 查询不同，仅限元数据的删除查询不会出现 `EXPLAIN` 中的执行步骤：

```
explain DELETE FROM my_external_schema.lineitem WHERE l_ship_date = '20251231';

 QUERY PLAN
------------
"XN Seq Scan Metadata of my_external_schema.lineitem location: "s3://s3-path//table-location" format:ICEBERG (cost=0.00..0.01 rows=0 width=0)"
(0 rows)
```

## UPDATE
<a name="iceberg-writes-update"></a>

Iceberg 表的 `UPDATE` 查询语法与 RMS 表的现有 `UPDATE` 语法非常相似：

```
[ WITH [RECURSIVE] {{common_table_expression}} [, {{common_table_expression}} , ...] ]
UPDATE {{iceberg_table}} [ [ AS ] alias ] SET column = { {{expression}} } [,...]
[ FROM {{fromlist}} ]
[ WHERE {{condition}} ]
```

对于 S3 表存储桶，您也可以使用三部分表示法。

```
[ WITH [RECURSIVE] {{common_table_expression}} [, {{common_table_expression}} , ...] ]
UPDATE "{{<table_bucket_name>}}@s3tablescatalog".{{<database_name>}}.{{<table_name>}} [ [ AS ] alias ] SET column = { {{expression}} } [,...]
[ FROM {{fromlist}} ]
[ WHERE {{condition}} ]
```

`{{iceberg_table}}` 可以使用 `{{<external_schema>}}.{{<external_table_name>}}` 格式进行引用，也可以使用三部分表示法来表示自动挂载目录。请参阅[在 Amazon Redshift 中引用 Iceberg 表](https://docs.aws.amazon.com/redshift/latest/dg/referencing-iceberg-tables.html)。

您可以通过引用其他表中的信息来更新表。在 FROM 子句中列出其他表，或者将子查询用作 WHERE 条件的一部分。源表可以是 Iceberg 表，也可以是 Amazon Redshift RMS 表。

`UPDATE` 也可以在分区表上运行。当 `UPDATE` 更改属于当前分区规范的列值时，新的更新行将根据新的更新值插入到新分区中。

例如，我们有一个如下所示的分区 Iceberg 表：

```
CREATE TABLE my_external_schema.lineitem
(l_item_id int,
 l_ship_date varchar,
 ...
)
USING ICEBERG
LOCATION ...
PARTITIONED BY l_ship_date;

INSERT INTO my_external_schema.lineitem VALUES (10099, '20251231', ...);
```

在运行如下更新查询时：

```
UPDATE my_external_schema.lineitem SET l_ship_date = '20260101'
WHERE l_item_id = 10099;
```

我们会将这个具有 `l_item_id` 10099 的行从分区移 `20251231` 到新分区 `20260101`。

同样重要的是要注意，`UPDATE` 可能有多个候选值。请考虑以下查询：

```
CREATE TABLE my_ext_schema.t1(x1 int, y1 int) USING ICEBERG LOCATION ...;
CREATE TABLE my_ext_schema.t2(x2 int, y2 int) USING ICEBERG LOCATION ...;
INSERT INTO my_ext_schema.t1 VALUES (1,10), (2,20), (3,30);
INSERT INTO my_ext_schema.t2 VALUES (2,40), (2,50);
UPDATE my_ext_schema.t1 SET y1=y2 FROM my_ext_schema.t2 WHERE x1=x2;
```

在这种情况下，y1 可以是 40 或 50。结果是不确定的。您可以将配置参数 `error_on_nondeterministic_update` 设置为 true，以便在这种情况发生时强制查询出错。这与现有的 RMS 表 `UPDATE` 行为一致。有关更多信息，请参阅 [error\_on\_nondeterministic\_update](https://docs.aws.amazon.com/redshift/latest/dg/r_error_on_nondeterministic_update.html)。

## MERGE
<a name="iceberg-writes-merge"></a>

`MERGE` 查询有条件地将源表中的行合并到目标表中。它与现有 RMS 表具有相同的 `MERGE` 查询语法：

```
MERGE INTO {{target_iceberg_table}} USING {{source_table}} [ [ AS ] {{alias}} ]
ON {{match_condition}}
[ WHEN MATCHED THEN { UPDATE SET {{col_name}} = { {{expr}} } [,...] | DELETE }
  WHEN NOT MATCHED THEN INSERT [ ( {{col_name}} [,...] ) ]
  VALUES ( { {{expr}} } [, ...] )
| REMOVE DUPLICATES ]
```

对于 S3 表存储桶，您也可以使用三部分表示法。

```
MERGE INTO "{{<table_bucket_name>}}@s3tablescatalog".{{<database_name>}}.{{<table_name>}} USING {{source_table}} [ [ AS ] {{alias}} ]
ON {{match_condition}}
[ WHEN MATCHED THEN { UPDATE SET {{col_name}} = { {{expr}} } [,...] | DELETE }
  WHEN NOT MATCHED THEN INSERT [ ( {{col_name}} [,...] ) ]
  VALUES ( { {{expr}} } [, ...] )
| REMOVE DUPLICATES ]
```

`{{target_iceberg_table}}` 可以使用 `{{<external_schema>}}.{{<external_table_name>}}` 格式进行引用，也可以使用三部分表示法来表示自动挂载目录。请参阅[在 Amazon Redshift 中引用 Iceberg 表](https://docs.aws.amazon.com/redshift/latest/dg/referencing-iceberg-tables.html)。

`{{source_table}}` 可以是 Iceberg 表，也可以是 Amazon Redshift RMS 表。

使用 `REMOVE DUPLICATES` 时，`MERGE` 命令使用简化模式。有关简化模式的更多详细信息，请参阅原始 `MERGE` [命令文档](https://docs.aws.amazon.com/redshift/latest/dg/r_MERGE.html)。

执行 `MERGE` 查询时，Amazon Redshift 会生成中间数据文件并将其存储在目标表的位置。这些文件将在查询结束时被垃圾回收。因此，`MERGE` 查询需要 Amazon S3 存储桶上的 `DELETE` 权限才能正常运行。如果垃圾回收操作失败，则会引发权限不足错误。对于 Amazon S3 表，垃圾回收由 Amazon S3 表类数据存储服务管理。因此，执行 `MERGE` 查询不需要 `DELETE` 权限。

## DROP TABLE
<a name="iceberg-writes-drop-table"></a>

要从目录中删除 Iceberg 表，请使用 `DROP TABLE` 命令：

```
DROP TABLE {{<external_schema>}}.{{<table_name>}}
```

您还可以对自动挂载目录使用三部分表示法：

```
DROP TABLE "{{<catalog_name>}}".{{<database_name>}}.{{<table_name>}}
```

使用 DROP 命令删除 Iceberg 表是仅对元数据执行的操作。如果表是 Amazon S3 表，则它会从 AWS Glue Data Catalog 和 Amazon S3 表目录中删除该表条目。Amazon Redshift 不会清理或删除表位置下的任何现有数据文件或元数据文件。您可以使用 AWS Glue 和 Amazon S3 表中的功能来移除孤立文件。对于 AWS Glue，请参阅[删除孤立文件](https://docs.aws.amazon.com/glue/latest/dg/orphan-file-deletion.html)。对于 Amazon S3 表，请参阅[表维护](https://docs.aws.amazon.com/AmazonS3/latest/userguide/s3-tables-maintenance.html)。