

# 通过 Athena 注册 S3 表存储桶目录和查询表
<a name="gdc-register-s3-table-bucket-cat"></a>

Amazon S3 表存储桶是 Amazon S3 中的一种存储桶类型，专门用于存储 Apache Iceberg 表中的表格数据。表存储桶可自动执行压缩、快照管理和垃圾回收等表管理任务，从而持续优化查询性能并尽可能降低成本。无论您是刚开始使用，还是已经在 Iceberg 环境中拥有数千个表，表存储桶都能简化任意规模的数据湖。有关更多信息，请参阅 [Table Buckets](https://docs.aws.amazon.com/AmazonS3/latest/userguide/s3-tables-buckets.html)。

## 注意事项和限制
<a name="gdc-register-s3-table-consideration"></a>
+ Iceberg 表支持的所有 DDL 操作都支持 S3 表，但以下情况除外：
  + 不支持 `ALTER TABLE RENAME`、`CREATE VIEW` 和 `ALTER DATABASE`。
  + `OPTIMIZE` 和 `VACUUM` – 您可以在 S3 中管理压缩和快照管理。有关更多信息，请参阅 [S3 表维护文档](https://docs.aws.amazon.com/AmazonS3/latest/userguide/s3-tables-maintenance.html)。
+ 不支持对注册为 Athena 数据来源的 S3 表进行 DDL 查询。
+ 不支持重复使用查询结果。
+ 在启用了 SSE-KMS、CSE-KMS 加密的工作组中，您无法在 S3 表上运行 `INSERT`、`UPDATE`、`DELETE` 或 `MERGE` 之类的写入操作。
+ 在启用了“S3 申请方付款”选项的工作组中，您无法在 S3 表上运行 DML 操作。

## 从 Athena 中查询 S3 表
<a name="gdc-register-s3-table-prereq-setup"></a>

**在 Athena 中查询 S3 表之前，请完成以下前提步骤**

1. 创建一个 S3 表存储桶。有关更多信息，请参阅《Amazon Simple Storage Service 用户指南》中的 [Creating a table bucket](https://docs.aws.amazon.com/AmazonS3/latest/userguide/s3-tables-buckets-create.html)。

1. 确保已成功将表存储桶与 AWS Glue Data Catalog 集成。有关所需权限和设置步骤，请参阅《AWS Glue 开发人员指南》中的 [S3 表类数据存储服务集成的先决条件](https://docs.aws.amazon.com/glue/latest/dg/s3tables-catalog-prerequisites.html)和[启用 S3 表类数据存储服务与 Glue Data Catalog 的集成](https://docs.aws.amazon.com/glue/latest/dg/enable-s3-tables-catalog-integration.html)。

1. 对于用于通过 Athena 运行查询的主体，请使用以下方法之一授予其对 S3 表类数据存储服务目录的权限：

   **选项 1：使用 IAM 权限**

   使用 IAM 访问控制时，您的主体需要同时拥有对 AWS Glue Data Catalog 资源和 Amazon S3 表类数据存储服务资源的权限。

   以下列表包含对 Athena 中的 S3 表类数据存储服务执行任何受支持的 DDL 或 DML 操作所需的所有 `s3tables` 权限：
   + `s3tables:GetTableBucket`
   + `s3tables:GetNamespace`
   + `s3tables:GetTable`
   + `s3tables:GetTableData`
   + `s3tables:PutTableData`
   + `s3tables:ListNamespaces`
   + `s3tables:ListTables`
   + `s3tables:DeleteNamespace`
   + `s3tables:DeleteTable`
   + `s3tables:CreateNamespace`
   + `s3tables:CreateTable`
   + `s3tables:UpdateTableMetadataLocation`

   将这些权限应用于特定 S3 表存储桶和 S3 表资源，或者使用 `*` 作为资源来授予对您账户中所有表存储桶和表的访问权限。这些权限可以与 [https://docs.aws.amazon.com/aws-managed-policy/latest/reference/AmazonAthenaFullAccess.html](https://docs.aws.amazon.com/aws-managed-policy/latest/reference/AmazonAthenaFullAccess.html) 托管式策略结合使用，以实现完整的功能。

   **选项 2：使用 Lake Formation 权限**

   或者，要实现精细的访问控制，您可以通过 Lake Formation 控制台或 AWS CLI 授予 Lake Formation 对 S3 表目录的权限。这需要将您的 S3 表存储桶注册为 Lake Formation 数据位置。有关更多信息，请参阅《Lake Formation 开发人员指南》中的[在 AWS Glue Data Catalog 中创建 Amazon S3 表类数据存储服务目录](https://docs.aws.amazon.com/lake-formation/latest/dg/create-s3-tables-catalog.html)。

------
#### [ AWS 管理控制台 ]

   1. 以数据湖管理员身份登录 AWS Lake Formation 控制台（地址为 [https://console.aws.amazon.com/lakeformation/](https://console.aws.amazon.com/lakeformation/)）。有关如何创建数据湖管理员的更多信息，请参阅 [Create a data lake administrator](https://docs.aws.amazon.com/lake-formation/latest/dg/initial-lf-config.html#create-data-lake-admin)。

   1. 在导航窗格中，选择**数据权限**，然后选择**授予**。

   1. 在**授予权限**页面的**主体**下，选择要用于从 Athena 提交查询的主体。

   1. 在 **LF 标签或目录资源**下，选择**命名 Data Catalog 资源**。

   1. 对于**目录**，请选择您通过集成表存储桶创建的 Glue 数据目录。例如，*<accoundID>*:s3tablescatalog/*amzn-s3-demo-bucket*。

   1. 对于**目录权限**，请选择**超级**。

   1. 选择**授权**。

------
#### [ AWS CLI ]

   使用 Lake Formation 数据湖管理员角色运行以下命令，以授予对您用于从 Athena 提交查询的主体的访问权限。

   ```
   aws lakeformation grant-permissions \
   --region <region (Example,us-east-1)> \
   --cli-input-json \
   '{
       "Principal": {
           "DataLakePrincipalIdentifier": "<user or role ARN (Example, arn:aws:iam::<Account ID>:role/ExampleRole>"
       },
       "Resource": {
           "Catalog": {
               "Id":"<Account ID>:s3tablescatalog/amzn-s3-demo-bucket"
           }
       },
       "Permissions": ["ALL"]
   }'
   ```

------

**提交对 S3 表的查询**

1. 使用上述获得授权的用户/角色从 Athena 提交 `CREATE DATABASE` 查询。在此示例中，`s3tablescatalog` 是通过集成创建的父级 Glue 数据目录，` s3tablescatalog/amzn-s3-demo-bucket` 是为每个 S3 表存储桶创建的子 Glue 数据目录。查询的方法有两种。

------
#### [ Option 1 ]

   直接从控制台或 AWS CLI 指定子 Glue 数据目录（`s3tablescatalog/amzn-s3-demo-bucket`）。

   **使用 AWS 管理控制台**

   1. 从 [https://console.aws.amazon.com/athena/](https://console.aws.amazon.com/athena/) 打开 Athena 控制台。

   1. 在左侧导航栏中，对于**数据来源名称**，选择 **AwsDataCatalog**。

   1. 对于**目录**，选择 **s3tablescatalog/*amzn-s3-demo-bucket***。

   1. 在查询编辑器中，输入类似于 `CREATE DATABASE test_namespace` 的查询。

   **使用 AWS CLI**

   运行如下命令。

   ```
   aws athena start-query-execution \ 
   --query-string 'CREATE DATABASE `test_namespace`' \ 
   --query-execution-context '{"Catalog": "s3tablescatalog/amzn-s3-demo-bucket"}' \
   --work-group "primary"
   ```

------
#### [ Option 2 ]

   通过 Athena 控制台从子 Glue 数据目录创建 Athena 数据目录，并在查询中将其指定为目录。有关更多信息，请参阅 [将 S3 表存储桶目录注册为 Athena 数据来源](#gdc-register-s3-table-console-steps)。

------

1. 通过您在上一步中创建的数据库，使用 `CREATE TABLE` 创建表。以下示例将在您先前于 `s3tablescatalog/amzn-s3-demo-bucket` Glue 目录中创建的 *`test_namespace`* 数据库中创建一个表。

------
#### [ AWS 管理控制台 ]

   1. 在左侧导航栏中，对于**数据来源名称**，选择 **AwsDataCatalog**。

   1. 对于**目录**，选择 **s3tablescatalog/*amzn-s3-demo-bucket***。

   1. 对于**数据库**，请选择 **test\$1namespace**。

   1. 在查询编辑器中，运行以下查询。

      ```
      CREATE TABLE daily_sales (
              sale_date date,
              product_category
              string, sales_amount double)
      PARTITIONED BY (month(sale_date))
      TBLPROPERTIES ('table_type' = 'iceberg')
      ```

------
#### [ AWS CLI ]

   运行如下命令。

   ```
   aws athena start-query-execution \
   --query-string "CREATE TABLE daily_sales (
           sale_date date,
           product_category
           string, sales_amount double)
   PARTITIONED BY (month(sale_date))
   TBLPROPERTIES ('table_type' = 'iceberg')" \
   --query-execution-context '{"Catalog": "s3tablescatalog/amzn-s3-demo-bucket", "Database":"test_namespace"}' \
   --work-group "primary"
   ```

------

1. 将数据插入您在之前的步骤中创建的表。

------
#### [ AWS 管理控制台 ]

   1. 在左侧导航栏中，对于**数据来源名称**，选择 **AwsDataCatalog**。

   1. 对于**目录**，选择 **s3tablescatalog/*amzn-s3-demo-bucket***。

   1. 对于**数据库**，请选择 **test\$1namespace**。

   1. 在查询编辑器中，运行以下查询。

      ```
      INSERT INTO daily_sales
      VALUES 
          (DATE '2024-01-15', 'Laptop', 900.00),
          (DATE '2024-01-15', 'Monitor', 250.00),
          (DATE '2024-01-16', 'Laptop', 1350.00),
          (DATE '2024-02-01', 'Monitor', 300.00);
      ```

------
#### [ AWS CLI ]

   运行如下命令。

   ```
   aws athena start-query-execution \
   --query-string "INSERT INTO \"s3tablescatalog/amzn-s3-demo-bucket\".test_namespace.daily_sales
   VALUES 
   (DATE '2024-01-15', 'Laptop', 900.00),
   (DATE '2024-01-15', 'Monitor', 250.00),
   (DATE '2024-01-16', 'Laptop', 1350.00),
   (DATE '2024-02-01', 'Monitor', 300.00)"\ 
   --work-group "primary"
   ```

------

1. 向表中插入数据后，您可以对其进行查询。

------
#### [ AWS 管理控制台 ]

   1. 在左侧导航栏中，对于**数据来源名称**，选择 **AwsDataCatalog**。

   1. 对于**目录**，选择 **s3tablescatalog/*amzn-s3-demo-bucket***。

   1. 对于**数据库**，请选择 **test\$1namespace**。

   1. 在查询编辑器中，运行以下查询。

      ```
      SELECT
          product_category,
          COUNT(*) AS units_sold,
          SUM(sales_amount) AS total_revenue,
          AVG(sales_amount) AS average_price
      FROM
          daily_sales
      WHERE
          sale_date BETWEEN DATE '2024-02-01' 
                       AND DATE '2024-02-29'
      GROUP BY
          product_category
      ORDER BY
          total_revenue DESC
      ```

------
#### [ AWS CLI ]

   运行如下命令。

   ```
   aws athena start-query-execution \
   --query-string "SELECT product_category,
       COUNT(*) AS units_sold,
       SUM(sales_amount) AS total_revenue,
       AVG(sales_amount) AS average_price
   FROM \"s3tablescatalog/amzn-s3-demo-bucket\".test_namespace.daily_sales
   WHERE sale_date BETWEEN DATE '2024-02-01' AND DATE '2024-02-29'
   GROUP BY product_category
   ORDER BY total_revenue DESC"\
   --work-group "primary"
   ```

------

## 在 Athena 中创建 S3 表
<a name="gdc-create-s3-tables-athena"></a>

Athena 支持在现有的 S3 表命名空间或使用 `CREATE DATABASE` 语句在 Athena 中创建的命名空间中创建表。要从 Athena 创建 S3 表，语法要与[创建常规 Iceberg 表](querying-iceberg-creating-tables.md)时的语法相同，只是无需指定 `LOCATION`，如以下示例所示。

```
CREATE TABLE
[db_name.]table_name (col_name data_type [COMMENT col_comment] [, ...] )
[PARTITIONED BY (col_name | transform, ... )]
[TBLPROPERTIES ([, property_name=property_value] )]
```

您也可以使用 CREATE TABLE AS SELECT（CTAS）语句创建 S3 表。有关更多信息，请参阅 [适用于 S3 表的 CTAS](#ctas-s3-tables)。

## 将 S3 表存储桶目录注册为 Athena 数据来源
<a name="gdc-register-s3-table-console-steps"></a>

要通过 Athena 控制台注册 S3 表存储桶目录，请执行以下步骤。

1. 从 [https://console.aws.amazon.com/athena/](https://console.aws.amazon.com/athena/) 打开 Athena 控制台。

1. 在导航窗格中，选择**数据来源和目录**。

1. 在**数据来源和目录**页面上，选择**创建数据来源**。

1. 在**选择数据来源**中，选择 **Amazon S3 - AWS Glue Data Catalog**。

1. 在 **AWS Glue Data Catalog** 部分中，对于**数据来源账户**，选择**此账户中的 AWS Glue Data Catalog**。

1. 对于**创建表或注册目录**，请选择**注册新 AWS Glue 目录**。

1. 在**数据来源详细信息**部分中，对于**数据来源名称**，请输入用于在 SQL 查询中指定数据来源的名称，或者使用生成的默认名称。

1. 对于**目录**，选择**浏览**来搜索同一账户中的 AWS Glue 目录列表。如果看不到任何现有目录，请通过 [AWS Glue 控制台](https://console.aws.amazon.com/glue/)创建一个。

1. 在**浏览 AWS Glue 目录**对话框中，选择要使用的目录，然后选中**选择**。

1. （可选）对于**标签**，输入要与该数据来源关联的键值对。

1. 选择**下一步**。

1. 在**检查并创建**页面上，验证输入的信息是否正确，然后选择**创建数据来源**。

## 适用于 S3 表的 CTAS
<a name="ctas-s3-tables"></a>

Amazon Athena 现在支持对 S3 表执行 CREATE TABLE AS SELECT（CTAS）操作。此功能允许您根据 SELECT 查询的结果创建新的 S3 表。

在为 S3 表创建 CTAS 查询时，与标准 Athena 表相比，有一些重要的区别：
+ 您必须省略位置属性，因为 S3 表会自动管理自己的存储位置。
+ `table_type` 属性的默认值为 `ICEBERG`，因此您无需在查询中显式指定该属性。
+ 如果您不指定格式，系统将自动使用 `PARQUET` 作为数据的默认格式。
+ 所有其他属性都遵循与常规 Iceberg 表相同的语法。

在使用 CTAS 创建 S3 表类数据存储服务之前，请确保您已在 IAM 或 AWS Lake Formation 中配置必要的权限。具体而言，您需要在 S3 表目录中创建表的权限。如果没有这些权限，您的 CTAS 操作将失败。

**注意**  
如果您的 CTAS 查询失败，您可能需要先使用 [S3 表 API](https://docs.aws.amazon.com/AmazonS3/latest/userguide/s3-tables-delete.html) 删除表，然后再尝试重新运行查询。您无法使用 Athena `DROP TABLE` 语句删除由该查询部分创建的表。

**示例**

```
CREATE TABLE "s3tablescatalog/amzn-s3-demo-bucket"."namespace"."s3-table-name"
WITH (
    format = 'PARQUET'
)
AS SELECT *
FROM source_table;
```