

# 在 Amazon Athena 中运行 SQL 查询
<a name="querying-athena-tables"></a>

您可以使用 Amazon Athena 对在 AWS Glue Data Catalog 中注册的以及诸如 Hive 元数据仓等数据源及您使用 Athena 联合查询功能连接的 Amazon DocumentDB 实例运行 SQL 查询。有关使用数据源的更多信息，请参阅[连接到数据来源](work-with-data-stores.md)。运行修改架构的数据定义语言(DDL)查询时，Athena 将元数据写入与数据源关联的元数据仓。此外，一些查询(如 `CREATE TABLE AS` 和 `INSERT INTO`)可以将记录写入数据集，例如，将 CSV 记录添加到 Amazon S3 位置。

本节提供了使用各种 SQL 语句对常见数据源和数据类型运行 Athena 查询的指导。为使用常见结构和运算符提供了一般指导，例如，如何使用数组、连接、过滤、拼合和排序。其他示例包括查询具有嵌套结构和映射的表、基于 JSON 编码数据集的表以及与 AWS CloudTrail 日志和 Amazon EMR 日志等 AWS 服务 关联的数据集中的数据。本文档讨论不包含标准 SQL 使用步骤的说明。有关 SQL 的更多信息，请参阅 [Trino](https://trino.io/docs/current/language.html) 和 [Presto](https://prestodb.io/docs/current/sql.html) 语言参考。

**Topics**
+ [查看查询计划](query-plans.md)
+ [使用查询结果和最近查询](querying.md)
+ [在 Athena 中重复使用查询结果](reusing-query-results.md)
+ [查看查询统计数据](query-stats.md)
+ [使用视图](views.md)
+ [使用已保存的查询](saved-queries.md)
+ [使用参数化查询](querying-with-prepared-statements.md)
+ [使用成本型优化器](cost-based-optimizer.md)
+ [查询 S3 Express One Zone](querying-express-one-zone.md)
+ [查询 Amazon Glacier](querying-glacier.md)
+ [处理架构更新](handling-schema-updates-chapter.md)
+ [查询数组](querying-arrays.md)
+ [查询地理空间数据](querying-geospatial-data.md)
+ [查询 JSON 数据](querying-JSON.md)
+ [将机器学习（ML）与 Athena 结合使用](querying-mlmodel.md)
+ [使用 UDF 进行查询](querying-udf.md)
+ [跨区域查询](querying-across-regions.md)
+ [查询 AWS Glue Data Catalog](querying-glue-catalog.md)
+ [查询 AWS 服务日志](querying-aws-service-logs.md)
+ [查询 Web 服务器日志](querying-web-server-logs.md)

有关注意事项和限制，请参阅[Amazon Athena 中 SQL 查询的注意事项和限制](other-notable-limitations.md)。

# 查看 SQL 查询的执行计划
<a name="query-plans"></a>

您可以使用 Athena 查询编辑器查看查询如何运行的图形展示。当您在编辑器中输入查询并选择 **Explain（说明）**选项时，Athena 使用查询上的 [EXPLAIN](athena-explain-statement.md) SQL 语句创建两个相应图表：分布式执行计划和逻辑执行计划。您可以使用这些图表对查询进行分析、排除故障并提高查询的效率。

**要查看查询的执行计划**

1. 在查询编辑器中输入您的查询，然后选择 **Explain（说明）**。  
![\[在 Athena 查询编辑器中选择 Explain（说明）。\]](http://docs.aws.amazon.com/zh_cn/athena/latest/ug/images/query-plans-1.png)

   **Distributed plan（分布式计划）**选项卡将显示分布式环境中查询的执行计划。分布式计划具有处理片段或*阶段*。每个阶段都有从零开始的索引编号，并由一个或多个节点进行处理。数据可以在节点之间进行交换。  
![\[示例查询分布式计划图表。\]](http://docs.aws.amazon.com/zh_cn/athena/latest/ug/images/query-plans-2.png)

1. 要导航图表，请使用以下选项：
   + 要放大或缩小，请滚动鼠标或使用放大图标。
   + 要调整图表以适合屏幕，请选择**缩放至适合**图标。
   + 要移动图表，请拖动鼠标指针。

1. 要查看阶段的详细信息，请选择阶段。  
![\[选择阶段以查看该阶段的详细信息。\]](http://docs.aws.amazon.com/zh_cn/athena/latest/ug/images/query-plans-3.png)

1. 要查看阶段的整体详细信息，请选择详细信息窗格右上角的展开图标。

1. 要查看更多详细信息，请展开运算符树中的一个或多个项目。有关分布式计划分段的信息，请参见 [EXPLAIN 语句输出类型](athena-explain-statement-understanding.md#athena-explain-statement-understanding-explain-plan-types)。  
![\[分布式查询计划中某个阶段的展开运算符树。\]](http://docs.aws.amazon.com/zh_cn/athena/latest/ug/images/query-plans-4.png)
**重要**  
目前，某些分区筛选器可能在嵌套运算符树图表中不可见，即使 Athena 确实将其应用于您的查询。要验证此类筛选的效果，请在您的查询中运行 [EXPLAIN](athena-explain-statement.md#athena-explain-statement-syntax-athena-engine-version-2) 或 [EXPLAIN ANALYZE](athena-explain-statement.md#athena-explain-analyze-statement) 并查看结果。

1. 选择 **Logical plan（逻辑计划）**选项卡。该图表显示了运行查询的逻辑计划。有关操作术语的更多信息，请参阅 [了解 Athena EXPLAIN 语句结果](athena-explain-statement-understanding.md)。  
![\[Athena 中的逻辑查询计划图表。\]](http://docs.aws.amazon.com/zh_cn/athena/latest/ug/images/query-plans-5.png)

1. 要将计划导出为 SVG 或 PNG 图像或 JSON 文本，请选择 **Export（导出）**。

## 其他资源
<a name="query-plans-additional-resources"></a>

有关详细信息，请参阅以下资源：

[在 Athena 中使用 EXPLAIN 和 EXPLAIN ANALYZE](athena-explain-statement.md)

[了解 Athena EXPLAIN 语句结果](athena-explain-statement-understanding.md)

[查看已完成查询的统计数据和执行详细信息](query-stats.md)

[![AWS Videos](http://img.youtube.com/vi/https://www.youtube.com/embed/7JUyTqglmNU/0.jpg)](http://www.youtube.com/watch?v=https://www.youtube.com/embed/7JUyTqglmNU)


# 使用查询结果和最近查询
<a name="querying"></a>

Amazon Athena 会将运行的每个查询的查询结果和查询执行结果元数据自动存储在*查询结果位置*（可以在 Amazon S3 中指定）中。如有必要，您可以在此位置访问这些文件以对其进行处理。您还可以直接从 Athena 控制台下载查询结果文件。

Athena 现在为您提供两种管理查询结果的选项；您可以使用客户拥有的 S3 存储桶，也可以选择托管查询结果功能。使用您自己的存储桶，您可以完全控制存储、权限、生命周期策略和保留，从而提供最大的灵活性，但需要更多管理。或者，当您选择托管查询结果选项时，该服务会自动处理存储和生命周期管理，从而无需配置单独的结果存储桶，也不会在预定的保留期后自动清理结果。有关更多信息，请参阅 [托管查询结果](managed-results.md)。

要首次设置 Amazon S3 查询结果位置，请参阅 [使用 Athena 控制台指定查询结果位置](query-results-specify-location-console.md)。

对于运行的每个查询，将自动保存输出文件。要使用 Athena 控制台访问和查看查询输出文件，IAM 主体（用户和角色）需要 Amazon S3 [GetObject](https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetObject.html) 操作的权限来获得查询结果位置，并需要 Athena [GetQueryResults](https://docs.aws.amazon.com/athena/latest/APIReference/API_GetQueryResults.html) 操作的权限。可对查询结果位置进行加密。如果该位置已加密，用户必须具有加密和解密查询结果位置的相应密钥权限。

**重要**  
有权限对查询结果位置执行 Amazon S3 `GetObject` 操作的 IAM 委托人可以从 Amazon S3 中检索查询结果，即使 Athena `GetQueryResults` 操作的权限被拒绝。

**注意**  
当查询被取消或失败时，Athena 可能已将部分结果写入 Amazon S3。在这种情况下，Athena 不会从存储结果的 Amazon S3 前缀中删除部分结果。您必须删除带有部分结果的 Amazon S3 前缀。Athena 使用 Amazon S3 分段上传来写入数据 Amazon S3。我们建议您设置存储桶生命周期策略，以便当查询失败时终止分段上传。有关更多信息，请参阅《Amazon Simple Storage Service 用户指南**》中的 [使用存储桶生命周期策略中止未完成的分段上传](https://docs.aws.amazon.com/AmazonS3/latest/userguide/mpuoverview.html#mpu-abort-incomplete-mpu-lifecycle-config)。
在某些情况下，Athena 可能会自动重试查询执行。在大多数情况下，这些查询能够成功完成，并将查询 ID 标记为 `Completed`。这些查询可能在初始尝试时写入了部分结果，并且可能会生成不完整的分段上传。

**Topics**
+ [托管查询结果](managed-results.md)
+ [指定查询结果位置](query-results-specify-location.md)
+ [使用 Athena 控制台下载查询结果文件](saving-query-results.md)
+ [在 Athena 控制台中查看最近的查询](queries-viewing-history.md)
+ [将多个最近的查询下载到 CSV 文件](queries-downloading-multiple-recent-queries-to-csv.md)
+ [配置最近的查询显示选项](queries-recent-queries-configuring-options.md)
+ [将查询历史记录保留 45 天以上](querying-keeping-query-history.md)
+ [在 Amazon S3 中查找查询输出文件](querying-finding-output-files.md)

# 托管查询结果
<a name="managed-results"></a>

使用托管查询结果，您可以运行 SQL 查询，而无需提供用于存储查询结果的 Amazon S3 存储桶。这使您无需预置、管理、控制访问和清理自己的 S3 存储桶。首先，创建新的工作组或编辑现有工作组。在**查询结果配置**下，选择 **Athena 托管**。

**主要 功能**
+ 通过无需在运行查询之前选择 S3 存储桶位置来简化工作流。
+ 使用托管查询结果无需额外费用，并且查询结果的自动删除可减少管理开销和单独的 S3 存储桶清理过程的需要。
+ 简单易上手：可轻松配置新的和预先存在的工作组以使用托管查询结果。您可以在 AWS 账户中混合使用 Athena 托管查询结果和客户管理的查询结果。
+ 简化了 IAM 权限，允许通过与各个工作组绑定的 `GetQueryResults` 和 `GetQueryResultsStream` 访问读取结果。
+ 查询结果将使用您选择的 AWS 拥有的密钥或客户拥有的密钥自动加密。

## 注意事项和限制
<a name="managed-results-considerations"></a>

****
+ 在 Athena 中，对查询结果的访问是在工作组级别进行管理的。为此，您需要对特定工作组的 `GetQueryResults` 和 `GetQueryResultsStream` IAM 操作具有显式权限。`GetQueryResults` 操作确定谁可以以分页格式检索已完成查询的结果，而 `GetQueryResultsStream` 操作确定谁可以流式传输已完成查询的结果（通常由 Athena 驱动程序使用）。
+ 您无法从控制台下载大于 200 MB 的查询结果文件。使用 `UNLOAD` 语句将大于 200 MB 的结果写入可单独下载的位置。
+ 托管查询结果功能不支持[查询结果重用](reusing-query-results.md)。
+ 查询结果 24 小时内可用。在此期间，查询结果将免费存储。此期限过后，查询结果将被自动删除。

## 使用托管查询结果创建或编辑工作组
<a name="using-managed-query-results"></a>

要从控制台创建工作组或使用托管查询结果更新现有工作组，请执行以下操作：

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

1. 从左侧导航中，选择**工作组**。

1. 选择**创建工作组**以创建新的工作组或编辑列表中的现有工作组。

1. 在**查询结果配置**下，选择 **Athena 托管**。  
![\[查询结果配置菜单。\]](http://docs.aws.amazon.com/zh_cn/athena/latest/ug/images/athena-managed.png)

1. 对于**加密查询结果**，选择所需的加密选项。有关更多信息，请参阅 [选择查询结果加密](#managed-query-results-encryption-at-rest)。

1. 填写所有其他必需的详细信息并选择**保存更改**。

## 选择查询结果加密
<a name="managed-query-results-encryption-at-rest"></a>

加密配置有两个选项：
+ **使用 AWS 拥有的密钥加密** – 这是使用托管查询结果时的默认选项。如果希望查询结果由 AWS 拥有的密钥加密，请选择此选项。
+ **使用客户自主管理型密钥加密** – 如果希望使用客户自主管理型密钥加密和解密查询结果，请选择此选项。要使用客户自主管理型密钥，请在密钥策略部分的 Principal 元素中添加 Athena 服务。有关更多信息，请参阅 [为托管查询结果设置 AWS KMS 密钥策略](#managed-query-results-set-up)。要成功运行查询，运行查询的用户需要访问 AWS KMS 密钥的权限。

## 为托管查询结果设置 AWS KMS 密钥策略
<a name="managed-query-results-set-up"></a>

密钥策略的 `Principal` 部分指定了谁可以使用此密钥。托管查询结果功能引入了必须在 `Principal` 部分中指定的主体 `encryption.athena.amazonaws.com`。此服务主体专门用于访问非 Athena 拥有的密钥。您还必须将 `kms:Decrypt`、`kms:GenerateDataKey` 和 `kms:DescribeKey` 操作添加到用于访问托管结果的密钥策略中。这三个操作是最低允许的操作。

托管查询结果使用您的工作组 ARN 作为[加密上下文](https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#encrypt_context)。由于 `Principal` 部分是一项 AWS 服务，因此您还需要在密钥策略条件中添加 `aws:sourceArn` 和 `aws:sourceAccount`。以下示例显示了对单个工作组具有最低权限的 AWS KMS 密钥策略。

```
 {
    "Sid": "Allow athena service principal to use the key",
    "Effect": "Allow",
    "Principal": {
        "Service": "encryption.athena.amazonaws.com"
    },
    "Action": [
        "kms:Decrypt",
        "kms:GenerateDataKey",
        "kms:DescribeKey"
      ],
    "Resource": "arn:aws:kms:us-east-1:{account-id}:key/{key-id}",
    "Condition": {
    "ArnLike": {
        "kms:EncryptionContext:aws:athena:arn": "arn:aws:athena:us-east-1:{account-id}:workgroup/{workgroup-name}",
        "aws:SourceArn": "arn:aws:athena:us-east-1:{account-id}:workgroup/{workgroup-name}"
    },
    "StringEquals": {
        "aws:SourceAccount": "{account-id}"
    }
}
```

以下示例 AWS KMS 密钥策略允许同一账户 *account-id* 内的所有工作组使用相同的 AWS KMS 密钥。

```
{
    "Sid": "Allow athena service principal to use the key",
    "Effect": "Allow",
    "Principal": {
        "Service": "encryption.athena.amazonaws.com"
    },
    "Action": [
        "kms:Decrypt",
        "kms:GenerateDataKey",
        "kms:DescribeKey"
    ],
    "Resource": "arn:aws:kms:us-east-1:account-id:key/{key-id}",
    "Condition": {
        "ArnLike": {
          "kms:EncryptionContext:aws:athena:arn": "arn:aws:athena:us-east-1:account-id:workgroup/*",
          "aws:SourceArn": "arn:aws:athena:us-east-1:account-id:workgroup/*"
        },
        "StringEquals": {
          "aws:SourceAccount": "account-id"
        }
    }
}
```

除了 Athena 和 Amazon S3 权限外，您还必须获得执行 `kms:GenerateDataKey` 和 `kms:Decrypt` 操作的权限。有关更多信息，请参阅 [Amazon S3 中加密数据的权限](encryption.md#permissions-for-encrypting-and-decrypting-data)。

有关托管查询结果加密的更多信息，请参阅[加密托管查询结果](encrypting-managed-results.md)。

# 加密托管查询结果
<a name="encrypting-managed-results"></a>

Athena 提供以下用于加密[托管查询结果](managed-results.md)的选项。

## 使用 AWS 拥有的密钥进行加密
<a name="encrypting-managed-results-aws-owned-key"></a>

这是使用托管查询结果时的默认选项。此选项表示您要使用 AWS 拥有的密钥对查询结果进行加密。AWS 拥有的密钥不会存储在您的 AWS 账户中，而是 AWS 拥有的 KMS 密钥集合的一部分。当您使用 AWS 拥有的密钥时，无需支付任何费用，并且它们不会计入您账户的 AWS KMS 配额。

## 使用 AWS KMS 客户自主管理型密钥进行加密
<a name="encrypting-managed-results-customer-managed-key"></a>

客户自主管理型密钥是在您的 AWS 账户中由您创建、拥有和管理的 KMS 密钥。您可以完全控制这些 KMS 密钥，包括建立和维护其密钥策略、IAM 策略和授权、启用和禁用它们、轮换其加密材料、添加标签、创建引用它们的别名以及安排删除它们。有关更多信息，请参阅[客户自主管理型密钥](https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#customer-cmk)。

## Athena 如何使用客户自主管理型密钥对结果进行加密
<a name="encrypting-managed-results-how-ate-uses-cmk"></a>

当您指定客户自主管理型密钥时，Athena 会在将查询结果存储在托管查询结果中时使用它来加密查询结果。当您调用 `GetQueryResults` 时，将使用相同的密钥来解密结果。当您将客户自主管理型密钥的状态设置为禁用或安排删除它时，它会阻止 Athena 和所有用户使用该密钥加密或解密结果。

Athena 使用信封加密和密钥层次结构来加密数据。您的 AWS KMS 加密密钥用于生成和解密此密钥层次结构的根密钥。

每个结果都使用加密时在工作组中配置的客户自主管理型密钥进行加密。将密钥切换到其他客户自主管理型密钥或 AWS 拥有的密钥不会使用新密钥重新加密现有结果。删除和禁用特定客户自主管理型密钥只会影响密钥加密的结果的解密。

Athena 需要访问您的加密密钥来执行 `kms:Decrypt`、`kms:GenerateDataKey` 和 `kms:DescribeKey` 操作以加密和解密结果。有关更多信息，请参阅 [Amazon S3 中加密数据的权限](encryption.md#permissions-for-encrypting-and-decrypting-data)。

除了 Athena 和 Amazon S3 权限之外，使用 `StartQueryExecution` API 提交查询并使用 `GetQueryResults` 读取结果的主体还必须对客户自主管理型密钥具有执行 `kms:Decrypt`、`kms:GenerateDataKey` 和 `kms:DescribeKey` 操作的权限。有关更多信息，请参阅[AWS KMS 中的密钥策略](https://docs.aws.amazon.com/kms/latest/developerguide/key-policies.html#key-policy-default-allow-users)。

# 指定查询结果位置
<a name="query-results-specify-location"></a>

Athena 使用的查询结果位置由工作组设置和*客户端设置*共同决定。客户端设置取决于您运行查询的方式。
+  如果使用 Athena 控制台运行查询，在导航栏中 **Settings**（设置）项下输入的 **Query result location**（查询结果位置）将决定客户端设置。
+ 如果您使用 Athena API 运行查询，则 [StartQueryExecution](https://docs.aws.amazon.com/athena/latest/APIReference/API_StartQueryExecution.html) 操作的 `OutputLocation` 参数将决定客户端设置。
+ 如果您使用 ODBC 或 JDBC 驱动程序来运行查询，则连接 URL 中指定的 `S3OutputLocation` 属性决定客户端设置。

**重要**  
当您使用 API 或使用 ODBC 或 JDBC 驱动程序运行查询时，控制台设置不适用。

每个工作组配置都有一个可启用的[Override client-side settings (覆盖客户端设置)](https://docs.aws.amazon.com/athena/latest/ug/workgroups-settings-override.html) 选项。如果启用此选项，当与工作组关联的 IAM 委托人运行该查询时，工作组设置优先于适用的客户端设置。

## 关于以前创建的默认位置
<a name="query-results-specify-location-previous-defaults"></a>

以前，在 Athena 中,如果您在运行查询时未为 **Query result location**（查询结果位置）指定值，并且查询结果位置设置未被工作组覆盖，则 Athena 会为您创建默认位置。默认位置为 `aws-athena-query-results-MyAcctID-MyRegion`，其中 *MyAcctID* 是运行查询的 IAM 委托人的 Amazon Web Services 账户 ID，*MyRegion* 是运行查询的区域（例如 `us-west-1`）。

现在，您必须指定查询结果位置或使用覆盖查询结果位置设置的工作组，然后才能在您的账户以前未使用 Athena 的区域中运行 Athena 查询。虽然 Athena 不再为您创建默认查询结果位置，但之前创建的默认 `aws-athena-query-results-MyAcctID-MyRegion` 位置仍然有效，您可以继续使用它们。

**Topics**
+ [关于以前创建的默认位置](#query-results-specify-location-previous-defaults)
+ [使用 Athena 控制台指定查询结果位置](query-results-specify-location-console.md)
+ [使用工作组指定查询结果位置](query-results-specify-location-workgroup.md)

# 使用 Athena 控制台指定查询结果位置
<a name="query-results-specify-location-console"></a>

在运行查询之前，必须指定 Amazon S3 中的查询结果存储桶位置，或者您必须使用已指定存储桶且其配置覆盖客户端设置的工作组。

**使用 Athena 控制台指定客户端设置查询结果位置**

1. [切换](switching-workgroups.md)到要为其指定查询结果位置的工作组。工作组的默认名称为 **primary**。

1. 从导航栏中选择 **Settings**（设置）。

1. 从导航栏中选择 **Manage**（管理）。

1. 对于 **Manage settings**（管理设置），执行以下操作之一：
   + 在 **Location of query result**（查询结果位置）文本框中，输入您在 Amazon S3 中为查询结果创建的存储桶路径。在路径前添加前缀 `s3://`。
   + 选择 **Browse S3**（浏览 S3），选择您为当前区域创建的 Amazon S3 存储桶，然后选择 **Choose**（选择）。
**注意**  
如果使用的工作组为工作组的所有用户指定查询结果位置，则更改查询结果位置的选项不可用。

1. （可选）选择 **View lifecycle configuration**（查看生命周期配置），以查看和配置查询结果存储桶上的 [Amazon S3 生命周期规则](https://docs.aws.amazon.com/AmazonS3/latest/userguide/object-lifecycle-mgmt.html)。您创建的 Amazon S3 生命周期规则可以是到期规则或转移规则。到期规则将在经过一定的时间后自动删除查询结果。转换规则会将查询结果转移到其他 Amazon S3 存储层。有关更多信息，请参阅《Amazon Simple Storage Service 用户指南》中的 [在存储桶上设置生命周期配置](https://docs.aws.amazon.com/AmazonS3/latest/userguide/how-to-set-lifecycle-configuration-intro.html)。

1. （可选）在 **Expected bucket owner**（预期存储桶拥有者）中，输入您希望成为输出位置存储桶的拥有者 AWS 账户 的 ID。这是附加安全措施。如果存储桶拥有者的账户 ID 与您在此处指定的 ID 不匹配，则输出到存储桶的尝试将失败。有关更多信息，请参阅《*Amazon S3 用户指南*》中的[使用存储桶拥有者条件验证存储桶所有权](https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucket-owner-condition.html)
**注意**  
预期存储桶拥有者设置仅适用于您为 Athena 查询结果指定的 Amazon S3 输出位置。其不适用于其他 Amazon S3 位置，例如外部 Amazon S3 存储桶中的数据源位置、`CTAS` 和 `INSERT INTO` 目标表位置、`UNLOAD` 语句输出位置、为联合查询溢出存储桶的操作，或针对另一个账户中的表运行的 `SELECT` 查询。

1. （可选）如果要加密存储在 Amazon S3 中的查询结果，则选择 **Encrypt query results**（加密查询结果）。要详细了解 Athena 的加密，请参阅[静态加密](encryption.md)。

1. （可选）如果查询结果存储桶[启用了 ACL](https://docs.aws.amazon.com/AmazonS3/latest/userguide/about-object-ownership.html)，则选择 **Assign bucket owner full control over query results**（为存储桶拥有者分配对查询结果的完全控制权），以向存储桶拥有者授予对查询结果的完全控制权。例如，假设您的查询结果位置属于其他账户所有，则可以将所有权以及对查询结果的完全控制权授予该其他账户。有关更多信息，请参阅《Amazon S3 用户指南》**中的[控制存储桶的对象所有权和禁用 ACL](https://docs.aws.amazon.com/AmazonS3/latest/userguide/about-object-ownership.html)。

1. 选择**保存**。

# 使用工作组指定查询结果位置
<a name="query-results-specify-location-workgroup"></a>

您使用 AWS 管理控制台、AWS CLI 或 Athena API 在工作组配置中指定查询结果位置。

使用 AWS CLI 时，请在运行 [https://docs.aws.amazon.com/cli/latest/reference/athena/create-work-group.html](https://docs.aws.amazon.com/cli/latest/reference/athena/create-work-group.html) 或者 [https://docs.aws.amazon.com/cli/latest/reference/athena/update-work-group.html](https://docs.aws.amazon.com/cli/latest/reference/athena/update-work-group.html) 命令时使用 `--configuration` 选项的 `OutputLocation` 参数指定查询结果位置。

**使用 Athena 控制台指定工作组的查询结果位置**

1. 如果控制台导航窗格不可见，请选择左侧的扩展菜单。  
![\[选择扩展菜单。\]](http://docs.aws.amazon.com/zh_cn/athena/latest/ug/images/nav-pane-expansion.png)

1. 在导航窗格中，选择 **Workgroups**（工作组）。

1. 在工作组列表中，选择要编辑的工作组的链接。

1. 选择**编辑**。

1. 对于 **Query result location and encryption**（查询结果位置和加密），请执行以下操作之一：
   + 在 **Location of query result**（查询结果位置）框中，输入 Amazon S3 中用于存储查询结果的存储桶的路径。在路径前添加前缀 `s3://`。
   + 选择 **Browse S3**（浏览 S3），选择为您要使用的当前区域创建的 Amazon S3 存储桶，然后选择 **Choose**（选择）。

1. （可选）在 **Expected bucket owner**（预期存储桶拥有者）中，输入您希望成为输出位置存储桶的拥有者 AWS 账户 的 ID。这是附加安全措施。如果存储桶拥有者的账户 ID 与您在此处指定的 ID 不匹配，则输出到存储桶的尝试将失败。有关更多信息，请参阅《*Amazon S3 用户指南*》中的[使用存储桶拥有者条件验证存储桶所有权](https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucket-owner-condition.html)
**注意**  
预期存储桶拥有者设置仅适用于您为 Athena 查询结果指定的 Amazon S3 输出位置。其不适用于其他 Amazon S3 位置，例如外部 Amazon S3 存储桶中的数据源位置、`CTAS` 和 `INSERT INTO` 目标表位置、`UNLOAD` 语句输出位置、为联合查询溢出存储桶的操作，或针对另一个账户中的表运行的 `SELECT` 查询。

1. （可选）如果要加密存储在 Amazon S3 中的查询结果，则选择 **Encrypt query results**（加密查询结果）。要详细了解 Athena 的加密，请参阅[静态加密](encryption.md)。

1. （可选）如果查询结果存储桶[启用了 ACL](https://docs.aws.amazon.com/AmazonS3/latest/userguide/about-object-ownership.html)，则选择 **Assign bucket owner full control over query results**（为存储桶拥有者分配对查询结果的完全控制权），以向存储桶拥有者授予对查询结果的完全控制权。例如，假设您的查询结果位置属于其他账户所有，则可以将所有权以及对查询结果的完全控制权授予该其他账户。

   如果存储桶的 S3 Object Ownership（S3 对象所有权）设置为 **Bucket owner preferred**（存储桶拥有者优先），则存储桶拥有者还拥有从此工作组写入的所有查询结果对象。例如，假设某个外部账户的工作组启用了此选项并将其查询结果位置设置为您账户的 Amazon S3 存储桶，并且该存储桶的 S3 Object Ownership（S3 对象所有权）设置为 **Bucket owner preferred**（存储桶拥有者优先），则您将拥有并完全控制该外部工作组的查询结果的访问权限。

   如果查询结果存储桶的 S3 Object Ownership（S3 对象所有权）设置为 **Bucket owner enforced**（存储桶拥有者强制），则选择此选项将不具有效力。有关更多信息，请参阅《Amazon S3 用户指南》**中的[控制存储桶的对象所有权和禁用 ACL](https://docs.aws.amazon.com/AmazonS3/latest/userguide/about-object-ownership.html)。

1. 如果您希望工作组的所有用户使用您指定的查询结果位置，请向下滚动到 **Settings**（设置）部分，然后选择 **Override client-side settings**（覆盖客户端侧设置）。

1. 选择**保存更改**。

# 使用 Athena 控制台下载查询结果文件
<a name="saving-query-results"></a>

运行查询后，您可以立即从查询窗格中下载查询结果 CSV 文件。您还可以从 **Recent queries**（最近的查询）选项卡下载最近查询的查询结果。

**注意**  
Athena 查询结果文件为数据文件，其中包含了可以被单个用户配置的信息。有些数据在用来读取和分析该数据的程序中有可能会将部分数据解释为命令（CSV 注入）。因此，将查询结果 CSV 数据导入到某个电子表格程序时，该程序可能警告您注意安全风险。为了确保系统安全，您应始终选择禁用下载查询结果中的链接或宏。

**运行查询并下载查询结果**

1. 在查询编辑器中输入您的查询，然后选择 **Run**（运行）。

   查询完成运行后，**Results (结果)** 窗格将显示查询结果。

1. 要下载查询结果的 CSV 文件，请选择查询结果窗格上方的 **Download results**（下载结果）。根据您的浏览器和浏览器配置，您可能需要确认下载。  
![\[将查询结果保存到 Athena 控制台中的 .csv 文件。\]](http://docs.aws.amazon.com/zh_cn/athena/latest/ug/images/getting-started-query-results-download-csv.png)

**下载早期查询的查询结果文件**

1. 选择 **Recent queries**（最近的查询）。  
![\[选择 Recent queries（最近的查询）以查看以前的查询。\]](http://docs.aws.amazon.com/zh_cn/athena/latest/ug/images/getting-started-recent-queries.png)

1. 使用搜索框查找查询，选择查询，然后选择 **Download results**（下载结果）。
**注意**  
您无法使用 **Download results**（下载结果）来选项检索已手动删除的查询结果，也无法检索已被 Amazon S3 [生命周期规则](https://docs.aws.amazon.com/AmazonS3/latest/userguide/how-to-set-lifecycle-configuration-intro.html) 删除或转移至其他位置的查询结果。  
![\[选择 Recent queries（最近的查询）以查找并下载以前的查询结果。\]](http://docs.aws.amazon.com/zh_cn/athena/latest/ug/images/querying-recent-queries-tab-download.png)

# 在 Athena 控制台中查看最近的查询
<a name="queries-viewing-history"></a>

您可以使用 Athena 控制台查看哪些查询成功或失败，并查看失败查询的错误详细信息。Athena 会将查询历史记录保留 45 天。

**在 Athena 控制台中查看最近的查询**

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

1. 选择 **Recent queries**（最近的查询）。**Recent queries**（最近的查询）选项卡显示有关运行的每个查询的信息。

1. 要在查询编辑器中打开查询语句，请选择查询的执行 ID。  
![\[选择查询的执行 ID 以在查询编辑器中进行查看。\]](http://docs.aws.amazon.com/zh_cn/athena/latest/ug/images/querying-view-query-statement.png)

1. 要查看失败查询的详细信息，请选择该查询的 **Failed**（失败）链接。  
![\[选择查询的 Failed（失败）链接，以查看有关该失败的信息。\]](http://docs.aws.amazon.com/zh_cn/athena/latest/ug/images/querying-view-query-failure-details.png)

# 将多个最近的查询下载到 CSV 文件
<a name="queries-downloading-multiple-recent-queries-to-csv"></a>

您可以使用 Athena 控制台的 **Recent queries**（最近查询）选项卡将一个或多个最近的查询导出到 CSV 文件，以便以表格格式进行查看。下载的文件不包含查询结果，而是包含 SQL 查询字符串本身及有关查询的其他信息。导出的字段包括执行 ID、查询字符串内容、查询开始时间、状态、运行时、扫描的数据量、使用的查询引擎版本和加密方法。您最多可以导出 500 个最近的查询，或者使用搜索框中输入的条件导出最多 500 个筛选后的查询。

**将一个或多个最近的查询导出到 CSV 文件**

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

1. 选择 **Recent queries**（最近的查询）。

1. （可选）使用搜索框筛选要下载的最近查询。

1. 选择**下载 CSV**。  
![\[选择下载 CSV。\]](http://docs.aws.amazon.com/zh_cn/athena/latest/ug/images/querying-recent-queries-csv.png)

1. 出现文件保存提示时，选择 **Save**（保存）。默认文件名为 `Recent Queries`，后跟时间戳（例如，`Recent Queries 2022-12-05T16 04 27.352-08 00.csv`）

# 配置最近的查询显示选项
<a name="queries-recent-queries-configuring-options"></a>

您可以配置 **Recent queries**（最近的查询）选项卡的选项，例如，要显示的列和文本换行。

**配置 **Recent queries**（最近的查询）选项卡的选项**

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

1. 选择 **Recent queries**（最近的查询）。

1. 选择选项按钮（齿轮图标）。  
![\[选择选项按钮，配置最近查询的显示。\]](http://docs.aws.amazon.com/zh_cn/athena/latest/ug/images/querying-recent-queries-options.png)

1. 在 **Preferences**（首选项）对话框中，选择每页行数、换行行为和要显示的列。  
![\[配置最近查询的显示。\]](http://docs.aws.amazon.com/zh_cn/athena/latest/ug/images/querying-recent-queries-preferences.png)

1. 选择**确认**。

# 将查询历史记录保留 45 天以上
<a name="querying-keeping-query-history"></a>

如果要将查询历史记录保留超过 45 天，您可以检索查询历史记录并将其保存到 Amazon S3 等数据存储中。要自动执行此过程，您可以使用 Athena 和 Amazon S3 API 操作和 CLI 命令。以下过程总结了这些步骤。

**以编程方式检索和保存查询历史记录**

1. 使用 Athena [ListQueryExecutions](https://docs.aws.amazon.com/athena/latest/APIReference/API_ListQueryExecutions.html) API 操作或 [list-query-executions](https://docs.aws.amazon.com/cli/latest/reference/athena/list-query-executions.html) CLI 命令检索查询 ID。

1. 使用 Athena [GetQueryExecution](https://docs.aws.amazon.com/athena/latest/APIReference/API_GetQueryExecution.html) API 操作或 [get-query-execution](https://docs.aws.amazon.com/cli/latest/reference/athena/get-query-execution.html) CLI 命令，根据查询的 ID 检索有关各个查询的信息。

1. 使用 Amazon S3 [PutObject](https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObject.html) API 操作或 [put-object](https://docs.aws.amazon.com/cli/latest/reference/s3api/put-object.html) CLI 命令将信息保存到 Amazon S3 中。

# 在 Amazon S3 中查找查询输出文件
<a name="querying-finding-output-files"></a>

查询输出文件存储在采用以下路径模式的 Amazon S3 子文件夹中，除非在其配置覆盖了客户端设置的工作组中进行查询。当工作组配置覆盖客户端设置时，查询将使用工作组指定的结果路径。

```
QueryResultsLocationInS3/[QueryName|Unsaved/yyyy/mm/dd/]
```
+ *QueryResultsLocationInS3* 是由工作组设置或客户端设置指定的查询结果位置。有关更多信息，请参阅本文后面的[指定查询结果位置](query-results-specify-location.md)。
+ 以下子文件夹仅为从控制台运行且工作组配置未覆盖其结果路径的查询而创建。从 AWS CLI 或使用 Athena API 运行的查询将直接保存到 *QueryResultsLocationInS3*。
  + *QueryName* 是要保存其结果的查询的名称。如果查询已运行但未保存，则使用 `Unsaved`。
  + *yyyy/mm/dd* 是查询运行的日期。

与 `CREATE TABLE AS SELECT` 查询关联的文件存储在以上模式的 `tables` 子文件夹中。

## 识别查询输出文件
<a name="querying-identifying-output-files"></a>

查询输出文件会按照查询名称、查询 ID 和查询运行日期保存到 Amazon S3 中的查询结果位置。每个查询的文件都使用 *QueryID* 命名，这是在每个查询运行时 Athena 为其分配的唯一标识符。

会保存以下文件类型：


| 文件类型 | 文件命名模式 | 说明 | 
| --- | --- | --- | 
|  **查询结果文件**  |  `QueryID.csv` `QueryID.txt`  |  DML 查询结果文件以逗号分隔值 (CSV) 格式保存。 DDL 查询结果保存为纯文本文件。 您可以在使用控制台时从控制台的 **Results**（结果）窗格下载这些文件，也可以从查询 **History**（历史记录）中下载。有关更多信息，请参阅 [使用 Athena 控制台下载查询结果文件](saving-query-results.md)。  | 
|  **查询元数据文件**  |  `QueryID.csv.metadata` `QueryID.txt.metadata`  |  DML 和 DDL 查询元数据文件以二进制格式保存，无法人为读取。文件扩展名对应于相关的查询结果文件。Athena 在使用 `GetQueryResults` 操作读取查询结果时会使用元数据。虽然可以删除这些文件，但我们不建议这样做，因为这样会丢失关于查询的重要信息。  | 
|  **数据清单文件**  |  `QueryID-manifest.csv`  |  生成数据清单文件以跟踪当 [INSERT INTO](insert-into.md) 查询运行时 Athena 在 Amazon S3 数据源位置创建的文件。如果查询失败，清单也将跟踪查询要写入的文件。清单对于识别由于查询失败而导致的孤立文件很有用。  | 

## 使用 AWS CLI 标识查询输出位置和文件
<a name="querying-finding-output-files-cli"></a>

要使用 AWS CLI 标识查询输出位置和结果文件，请运行 `aws athena get-query-execution` 命令，如以下示例所示。用查询 ID 替换 *abc1234d-5efg-67hi-jklm-89n0op12qr34*。

```
aws athena get-query-execution --query-execution-id abc1234d-5efg-67hi-jklm-89n0op12qr34
```

该命令返回的输出类似于下方内容。有关每个输出参数的说明，请参阅 *AWS CLI 命令参考*中的 [get-query-execution](https://docs.aws.amazon.com/cli/latest/reference/athena/get-query-execution.html)。

```
{
    "QueryExecution": {
        "Status": {
            "SubmissionDateTime": 1565649050.175,
            "State": "SUCCEEDED",
            "CompletionDateTime": 1565649056.6229999
        },
        "Statistics": {
            "DataScannedInBytes": 5944497,
            "DataManifestLocation": "s3://amzn-s3-demo-bucket/athena-query-results-123456789012-us-west-1/MyInsertQuery/2019/08/12/abc1234d-5efg-67hi-jklm-89n0op12qr34-manifest.csv",
            "EngineExecutionTimeInMillis": 5209
        },
        "ResultConfiguration": {
            "EncryptionConfiguration": {
                "EncryptionOption": "SSE_S3"
            },
            "OutputLocation": "s3://amzn-s3-demo-bucket/athena-query-results-123456789012-us-west-1/MyInsertQuery/2019/08/12/abc1234d-5efg-67hi-jklm-89n0op12qr34"
        },
        "QueryExecutionId": "abc1234d-5efg-67hi-jklm-89n0op12qr34",
        "QueryExecutionContext": {},
        "Query": "INSERT INTO mydb.elb_log_backup SELECT * FROM mydb.elb_logs LIMIT 100",
        "StatementType": "DML",
        "WorkGroup": "primary"
    }
}
```

# 在 Athena 中重复使用查询结果
<a name="reusing-query-results"></a>

在 Athena 中重新运行查询时，可以选择重用上次存储的查询结果。此选项可以提高性能并降低扫描字节数方面的成本。重用查询结果非常有用，例如，如果您知道结果在指定时间范围内不会发生变化，则可以指定重用查询结果的最大期限。只要存储结果不超过指定期限，Athena 就会使用该结果。有关更多信息，请参阅 *AWS 大数据博客*中的[使用 Amazon Athena 降低成本并提高查询性能](https://aws.amazon.com/blogs/big-data/reduce-cost-and-improve-query-performance-with-amazon-athena-query-result-reuse/)。

## 主要 功能
<a name="reusing-query-results-key-features"></a>

当您为查询启用结果重用时，Athena 会在同一工作组中查找该查询的先前执行情况。如果 Athena 找到匹配项，它将绕过执行并返回上一个匹配执行的查询结果。您可以针对每个查询启用查询结果重用。

满足以下所有条件时，Athena 将重复使用上一次查询的结果：
+ 查询字符串匹配由 Athena 确定。
+ 数据库与目录名称匹配。
+ 先前的结果尚未过期。
+ 查询结果配置与上一次执行的查询结果配置相匹配。
+ 您可以访问查询中引用的所有表。
+ 您可以访问存储先前结果的 S3 文件位置。

如果不满足这些条件中的任何一个，Athena 将在不使用缓存结果的情况下运行查询。

## 注意事项和限制
<a name="reusing-query-results-considerations-and-limitations"></a>

使用查询结果重用功能时，请记住以下几点：
+ Athena 仅在同一工作组内重用查询结果。
+ 重用查询结果功能遵循工作组配置。如果您覆盖查询的结果配置，该功能将被禁用。
+ 仅支持在 Amazon S3 上生成结果集的查询。不支持 `SELECT` 和 `EXECUTE` 以外的语句。
+ 支持向 AWS Glue 注册 Apache Hive、Apache Hudi、Apache Iceberg 和 Linux Foundation Delta Lake 表。不支持外部 Hive 元存储。
+ 不支持引用联合目录或外部 Hive 元存储的查询。
+ Lake Formation 受管表不支持查询结果重用。
+ 在 Lake Formation 中将表源的 Amazon S3 位置注册为数据位置时，不支持重用查询结果。
+ 不支持具有行和列权限的表。
+ 不支持具有精细访问控制（例如，列或行筛选）的表。
+ 任何引用不支持的表的查询都不能重用查询结果。
+ Athena 要求您具有 Amazon S3 读取权限，以便重用先前生成的输出文件。
+ 重用查询结果功能假定先前结果的内容未被修改。Athena 在使用先前的结果之前，不会检查其完整性。
+ 如果先前执行的查询结果已被删除或移至 Amazon S3 中的其他位置，则随后执行的同一查询将不会重用查询结果。
+ 因而可能会返回过时的结果。在达到指定的最大重用期限之前，Athena 不会检查源数据是否更改。
+ 如果有多个结果可供重用，Athena 会使用最新的结果。
+ 使用非确定性运算符或函数（例如 `rand()` 或 `shuffle()`）的查询不使用缓存结果。例如，不包含 `ORDER BY` 的 `LIMIT` 是不确定性的，因而不会缓存，但包含 `ORDER BY` 的 `LIMIT` 是确定性的，所以会缓存。
+ 要将查询结果重用功能与 JDBC 结合使用，所需的最低驱动程序版本为 2.0.34.1000。对于 ODBC，所需的最低驱动程序版本为 1.1.19.1002。有关驱动程序下载信息，请参阅 [通过 ODBC 和 JDBC 驱动程序连接到 Amazon Athena](athena-bi-tools-jdbc-odbc.md)。
+ 对于使用多个数据目录的查询，不支持查询结果重用。
+  对于包含超过 20 个表的查询，不支持查询结果重用。
+ 对于小于 100 KB 的查询字符串，注释和空格之间的差异将被忽略，并且 `INNER JOIN` 和 `JOIN` 被视为等效项，以便重复使用结果。大于 100 KB 的查询字符串必须完全匹配才能重复使用结果。
+ 如果查询结果超出指定的最大期限，或未指定最大期限时超出 60 分钟的默认值，则查询结果被视为过期。可以指定重用查询结果的最大期限（以分钟、小时或天为单位）。无论使用何种时间单位，可指定的最大期限均为 7 天。
+ 不支持[托管查询结果](https://docs.aws.amazon.com/athena/latest/ug/managed-results.html)。

## 如何在 Athena 控制台中重复使用查询结果
<a name="reusing-query-results-athena-console"></a>

要使用该功能，请在 Athena 查询编辑器中启用 **Reuse query results**（重用查询结果）选项。

![\[在 Athena 查询编辑器中启用 Reuse query results（重用查询结果）。\]](http://docs.aws.amazon.com/zh_cn/athena/latest/ug/images/reusing-query-results-1.png)


**配置重用查询结果功能**

1. 在 Athena 查询编辑器中的 **Reuse query results**（重用查询结果）选项下，选择 **up to 60 minutes ago**（截至 60 分钟前）旁边的编辑图标。

1. 在 **Edit reuse time**（编辑重用时间）对话框中，从右侧的框中选择一个时间单位（分钟、小时或天）。

1. 在左侧的框中，输入或选择要指定的时间单位数。无论选择何种时间单位，可输入的最大时间均为七天。  
![\[配置重用查询结果的最大期限。\]](http://docs.aws.amazon.com/zh_cn/athena/latest/ug/images/reusing-query-results-2.png)

1. 选择**确认**。

   系统会显示一条横幅确认您的配置更改，同时 **Reuse query results**（重用查询结果）选项会显示新设置。

# 查看已完成查询的统计数据和执行详细信息
<a name="query-stats"></a>

运行查询后，您可以获得处理的输入和输出数据的统计信息，查看查询每个阶段所用时间的图形展示，并以交互方式浏览执行详细信息。

**查看已完成查询的查询统计信息**

1. 在 Athena 查询编辑器中运行查询后，请选择 **Query stats（查询统计信息）**选项卡。  
![\[选择 Query stats（查询统计信息）。\]](http://docs.aws.amazon.com/zh_cn/athena/latest/ug/images/query-stats-1.png)

   **Query stats（查询统计信息）**选项卡提供以下信息：
   + **已处理数据** — 显示已处理的输入行数和字节数，以及输出的行数和字节数。
   + **运行时合计** — 显示运行查询所用的总时间，以及这段时间内用于排队、计划、执行和服务处理的时间的图形展示。
**注意**  
如果查询具有在 Lake Formation 中定义的行级筛选条件，不会显示阶段级输入和输出行数以及数据大小信息。

1. 要交互式浏览查询的运行方式的相关信息，请选择 **Execution details（执行详细信息）**。  
![\[选择 Execution details（执行详细信息）。\]](http://docs.aws.amazon.com/zh_cn/athena/latest/ug/images/query-stats-2.png)

   **Execution details（执行详细信息）**页面显示查询的执行 ID 和查询中从零开始的各阶段图表。各个阶段从下到上按顺序从开始到结束排列。每个阶段的标签都显示了阶段运行所花费的总时间。
**注意**  
查询的总运行时间和执行阶段时间之间通常会有很大差异。例如，以分钟为单位的总运行时间的查询，可以显示以小时为单位的某个阶段的执行时间。由于阶段是跨多个任务并行执行的逻辑计算单元，所以阶段的执行时间就是其所有任务的总执行时间。虽然存在这种差异，但阶段的执行时间可以作为查询中计算密集度最高的阶段的相对指标。  
![\[执行详细信息页面。\]](http://docs.aws.amazon.com/zh_cn/athena/latest/ug/images/query-stats-3.png)

   要导航图表，请使用以下选项：
   + 要放大或缩小，请滚动鼠标或使用放大图标。
   + 要调整图表以适合屏幕，请选择**缩放至适合**图标。
   + 要移动图表，请拖动鼠标指针。

1. 要查看阶段的更多详细信息，请选择阶段。右侧的阶段详细信息窗格显示输入和输出的行数和字节数，以及运算符树。  
![\[阶段详细信息窗格。\]](http://docs.aws.amazon.com/zh_cn/athena/latest/ug/images/query-stats-4.png)

1. 要查看阶段的整体详细信息，请选择详细信息窗格右上角的展开图标。

1. 要获取有关阶段各部分的信息，请展开运算符树中的一个或多个项目。  
![\[展开的运算符树。\]](http://docs.aws.amazon.com/zh_cn/athena/latest/ug/images/query-stats-5.png)

有关执行详情的更多信息，请参阅 [了解 Athena EXPLAIN 语句结果](athena-explain-statement-understanding.md)。

## 其他资源
<a name="query-stats-additional-resources"></a>

有关详细信息，请参阅以下资源：

[查看 SQL 查询的执行计划](query-plans.md)

[在 Athena 中使用 EXPLAIN 和 EXPLAIN ANALYZE](athena-explain-statement.md)

[![AWS Videos](http://img.youtube.com/vi/https://www.youtube.com/embed/7JUyTqglmNU/0.jpg)](http://www.youtube.com/watch?v=https://www.youtube.com/embed/7JUyTqglmNU)


# 使用视图
<a name="views"></a>

在 Amazon Athena 中，视图是逻辑表，而非物理表。每次在查询中引用视图时，定义该视图的查询都会运行。您可以从 `SELECT` 查询创建视图，然后在未来的查询中引用该视图。

您可以在 Athena 中使用两种不同的视图：Athena 视图和 AWS Glue Data Catalog 视图。

## 何时使用 Athena 视图？
<a name="when-to-use-views"></a>

您可能希望创建 Athena 视图以达到以下目的：
+ **查询数据子集**：例如，您可以根据原始表创建一个包含列子集的视图，以简化查询数据。
+ **合并表**：您可以使用视图将多个表合并为一个查询。如果您有多个表，希望使用 `UNION ALL` 将它们组合起来，可以创建一个视图，利用表达式简化针对组合表的查询。
+ **隐藏复杂性**：使用视图隐藏现有基本查询的复杂性和简化用户运行的查询。基本查询通常包括表之间的联接、列列表中的表达式和其他 SQL 语法，这使理解和调试查询变得困难。您可以创建一个能够隐藏复杂性和简化查询的视图。
+ **优化查询**：您可以使用视图来试验优化技术，以创建优化的查询。例如，如果您发现某种 `WHERE` 条件、`JOIN` 命令或其他表达式的组合能带来最佳性能，则可以使用这些子句和表达式创建一个视图。应用程序然后可以针对此视图执行相对简单的查询。如果后来您又发现更好的优化原始查询的办法，则当您重新创建视图时，所有应用程序会立即利用优化的基本查询。
+ **隐藏底层名称**：您可以使用视图隐藏底层表和列名称，并最大程度减少维护问题（如果这些名称改变）。如果名称发生变化，只需使用新名称重新创建视图即可。使用该视图而非表的查询直接保持运行，无需更改。

  有关更多信息，请参阅 [使用 Athena 视图](views-console.md)。

## 何时使用 AWS Glue Data Catalog 视图？
<a name="when-to-use-views-gdc"></a>

当您想跨 AWS 服务（例如 Amazon Athena 和 Amazon Redshift）使用单一通用视图时，请使用 AWS Glue Data Catalog 视图。在 Data Catalog 视图中，访问权限由创建视图的用户（而不是查询视图的用户）定义。这种授权方法称为*定义程序*语义。

以下使用案例展示如何使用 Data Catalog 视图。
+ **优化访问控制** – 您可以创建视图，根据用户所需的权限级别来限制数据访问。例如，您可以使用 Data Catalog 视图来防止非人力资源（HR）部门的员工查看个人身份信息。
+ **确保记录完整** – 通过对 Data Catalog 视图应用某些筛选器，您可以确保 Data Catalog 视图中的数据记录始终完整。
+ **增强安全性** – 在 Data Catalog 视图中，用于创建视图的查询定义必须完好无损，才能创建视图。这样的话，Data Catalog 视图不易受到恶意行为者的 SQL 命令的影响。
+ **防止访问基础表** – 定义程序语义允许用户访问视图，而无需向他们提供基础表。只有定义视图的用户才需要访问表。

Data Catalog 视图定义存储于 AWS Glue Data Catalog。这意味着，您可以使用 AWS Lake Formation 通过资源授权、列授权或基于标签的访问控制来授予访问权限。有关在 Lake Formation 中授予和撤消访问权限的更多信息，请参阅《AWS Lake Formation Developer Guide》**中的 [Granting and revoking permissions on Data Catalog resources](https://docs.aws.amazon.com/lake-formation/latest/dg/granting-catalog-permissions.html)。

有关更多信息，请参阅 [在 Athena 中使用 Data Catalog 视图](views-glue.md)。

# 使用 Athena 视图
<a name="views-console"></a>

可在 Athena 控制台中轻松创建、更新和管理 Athena 视图。

## 创建视图
<a name="creating-views"></a>

您可以通过使用模板或运行现有查询以在 Athena 控制台中创建视图。

**使用模板创建视图**

1. 在 Athena 控制台中，选择 **Tables and views**（表和视图）旁边的 **Create**（创建），然后选择 **Create view**（创建视图）。  
![\[创建视图。\]](http://docs.aws.amazon.com/zh_cn/athena/latest/ug/images/create-view.png)

   此操作会将可编辑的视图模板放入查询编辑器中。

1. 根据您的要求编辑视图模板。在语句中输入视图的名称时，请记住，视图名称不能包含除下划线 (`(_)`) 以外的特殊字符。请参阅[命名数据库、表和列](tables-databases-columns-names.md)。避免使用[转义查询中的保留关键字](reserved-words.md)来命名视图。

   有关创建视图的更多信息，请参阅 [CREATE VIEW 和 CREATE PROTECTED MULTI DIALECT VIEW](create-view.md) 和 [Athena 视图示例](views-examples.md)。

1. 选择 **Run**（运行）以创建视图。该视图将显示在 Athena 控制台中的视图列表中。

**通过现有查询创建视图**

1. 使用 Athena 查询编辑器运行现有查询。

1. 在查询编辑器窗口下，选择 **Create**（创建），然后选择 **View from query**（通过查询查看）。  
![\[选择 Create（创建），View from query（通过查询查看）。\]](http://docs.aws.amazon.com/zh_cn/athena/latest/ug/images/create-view-from-query.png)

1. 在 **Create View**（创建视图）对话框中，输入视图名称并选择 **Create**（创建）。视图名称不能包含除下划线 `(_)` 以外的特殊字符。请参阅[命名数据库、表和列](tables-databases-columns-names.md)。避免使用[转义查询中的保留关键字](reserved-words.md)来命名视图。

   Athena 将视图添加到控制台的视图列表中，并显示查询编辑器中视图的 `CREATE VIEW` 语句。

**备注**
+ 如果删除了作为其他表的依据的表，并且尝试运行该视图，Athena 将显示一条错误消息。
+ 可以创建一个嵌套视图，即在现有视图之上的视图。Athena 可防止您运行引用自身的递归视图。

# Athena 视图示例
<a name="views-examples"></a>

要显示视图查询的语法，请使用 [SHOW CREATE VIEW](show-create-view.md)。

**Example 示例 1**  
请考虑以下两个表：表 `employees`，具有两列，分别是 `id` 和 `name`，以及表 `salaries`，也具有两列，分别是 `id` 和 `salary`。  
在本示例中，我们创建名为 `name_salary` 的视图作为 `SELECT` 查询，该查询将获得一个 ID 列表，这些 ID 映射到来自表 `employees` 和 `salaries` 的薪水：  

```
CREATE VIEW name_salary AS
SELECT
 employees.name, 
 salaries.salary 
FROM employees, salaries 
WHERE employees.id = salaries.id
```

**Example 示例 2**  
在以下示例中，我们创建一个名为 `view1` 的视图，以使您能够隐藏较复杂的查询语法。  
该视图运行在两个表，即 `table1` 和 `table2` 上，其中每个表都是不同的 `SELECT` 查询。该视图选择来自 `table1` 的列并将结果与 `table2` 联接。联接基于在两个表中都存在的列 `a`。  

```
CREATE VIEW view1 AS
WITH
  table1 AS (
         SELECT a, 
         MAX(b) AS the_max 
         FROM x 
         GROUP BY a
         ),
  table2 AS (
         SELECT a, 
         AVG(d) AS the_avg 
         FROM y 
         GROUP BY a)
SELECT table1.a, table1.the_max, table2.the_avg
FROM table1
JOIN table2 
ON table1.a = table2.a;
```

有关查询联合视图的信息，请参阅 [查询联合视图](running-federated-queries.md#running-federated-queries-federated-views)。

# 管理 Athena 视图
<a name="views-managing"></a>

在 Athena 控制台中，您可以：
+ 在列出表的左窗格中找到所有视图。
+ 筛选视图。
+ 预览视图、显示其属性、编辑或删除视图。

**显示视图的操作**

只有创建了视图后，该视图才会出现在控制台中。

1. 在 Athena 控制台中，选择 **Views**（视图），然后选择一个视图，展开它并显示视图中的列。

1. 选择视图旁边的三个竖直的点，以显示视图的操作列表。  
![\[视图的操作菜单。\]](http://docs.aws.amazon.com/zh_cn/athena/latest/ug/images/view-options.png)

1. 选择操作以预览视图、将视图名称插入查询编辑器、删除视图、查看视图属性，或在查询编辑器中显示并编辑视图。

## Athena 视图支持的 DDL 操作
<a name="views-supported-actions"></a>

Athena 支持下列视图管理操作。


| 语句 | 说明 | 
| --- | --- | 
| [CREATE VIEW 和 CREATE PROTECTED MULTI DIALECT VIEW](create-view.md) |  从指定的 `SELECT` 查询创建新视图。有关更多信息，请参阅 [创建视图](views-console.md#creating-views)。 可选的 `OR REPLACE` 子句允许您通过替换来更新现有视图。  | 
| [DESCRIBE VIEW](describe-view.md) |  显示命名视图的列列表。这能让您检查复杂视图的属性。  | 
| [DROP VIEW](drop-view.md) |  删除现有视图。如果该视图不存在，可选 `IF EXISTS` 子句将抑制错误出现。  | 
| [SHOW CREATE VIEW](show-create-view.md) |  显示创建指定视图的 SQL 语句。  | 
| [SHOW VIEWS](show-views.md) |  列出指定数据库或当前数据库（如果省略数据库的名称）中的视图。将可选 `LIKE` 子句与一个正则表达式结合使用来限制视图名称列表。您还可以在控制台的左窗格中看到视图列表。  | 
| [SHOW COLUMNS](show-columns.md) |  列出视图的架构中的列。  | 

# Athena 视图注意事项和限制
<a name="considerations-limitations-views"></a>

Athena 视图具有以下注意事项和限制。

## 注意事项
<a name="considerations-views"></a>

以下注意事项适用于在 Athena 中创建和使用视图：
+ 在 Athena 中，您可以预览和使用在 Athena 控制台、AWS Glue Data Catalog 中或通过运行于连接到同一目录的 Amazon EMR 集群上的 Presto 创建的视图。
+ 如果您已在 Data Catalog 中创建了 Athena 视图，则数据目录会将视图视为表。您可以使用在 Data Catalog 中表级精细访问控制来[限制对这些视图的访问](fine-grained-access-to-glue-resources.md)。
+  Athena 可防止您运行递归视图，如有运行，则会显示错误消息。递归视图是引用自身的视图查询。
+ Athena 在检测到过时视图时会显示错误消息。发生以下一种情况时，会报告过时的视图：
  + 视图引用了不存在的表或数据库。
  + 在引用的表中进行了架构或元数据更改。
  + 删除了引用的表并使用不同的架构或配置重新创建。
+ 您可以创建并运行嵌套视图，只要嵌套视图背后的查询有效以及表和数据库存在。

## 限制
<a name="limitations-views"></a>
+ Athena 视图名称不能包含下划线 `(_)` 之外的特殊字符。有关更多信息，请参阅 [命名数据库、表和列](tables-databases-columns-names.md)。
+ 避免使用保留关键字来命名视图。如果使用保留关键字，请在视图查询中使用双引号将保留关键字括起来。请参阅[转义查询中的保留关键字](reserved-words.md)。
+ 您不能将 Athena 中创建的视图与外部 Hive 元存储或 UDF 结合使用。有关使用在 Hive 外部创建的视图的信息，请参阅 [使用 Hive 视图](hive-views.md)。
+ 您不能将视图与地理空间函数结合使用。
+ 您无法使用视图管理 Amazon S3 中数据的访问控制。要查询视图，您需要相应权限才能访问存储在 Amazon S3 中的数据。有关更多信息，请参阅 [控制从 Athena 对 Amazon S3 的访问](s3-permissions.md)。
+ 虽然 Athena 引擎版本 3 支持跨账户查询视图，但无法创建包含跨账户 AWS Glue Data Catalog 的视图。有关访问跨账户数据目录的信息，请参阅 [配置 AWS Glue 数据目录的跨账户存取](security-iam-cross-account-glue-catalog-access.md)。
+ Athena 视图不支持 Hive 或 Iceberg 隐藏的元数据列 `$bucket`、`$file_modified_time`、`$file_size` 和 `$partition`。有关在 Athena 中使用 `$path` 元数据列的信息，请参阅 [获取 Amazon S3 中源数据的文件位置](select.md#select-path)。

# 在 Athena 中使用 Data Catalog 视图
<a name="views-glue"></a>

在 Amazon Athena 中创建 Data Catalog 视图需要特殊的 `CREATE VIEW` 语句。查询它们将使用传统的 SQL `SELECT` 语法。Data Catalog 视图也称为*多方言*视图或 MDV。

## 创建 Data Catalog 视图
<a name="views-glue-creating-a-data-catalog-view"></a>

要在 Athena 中创建 Data Catalog 视图，请使用以下语法。

```
CREATE [ OR REPLACE ] PROTECTED MULTI DIALECT VIEW view_name 
SECURITY DEFINER 
[ SHOW VIEW JSON ]
AS athena-sql-statement
```

**注意**  
`SHOW VIEW JSON` 选项仅适用于 Data Catalog 视图，不适用于 Athena 视图。使用 `SHOW VIEW JSON` 选项执行“空运行”，以验证输入，如果验证成功，则返回将代表视图的 AWS Glue 表对象的 JSON。实际视图未创建。如果未指定 `SHOW VIEW JSON` 选项，则会进行验证，并在 Data Catalo 中照常创建视图。

以下示例展示 `Definer` 角色的用户如何创建 `orders_by_date` Data Catalog 视图。该示例假设 `Definer` 角色对 `default` 数据库中的 `orders` 表具有完全 `SELECT` 权限。

```
CREATE PROTECTED MULTI DIALECT VIEW orders_by_date 
SECURITY DEFINER 
AS 
SELECT orderdate, sum(totalprice) AS price 
FROM orders 
WHERE order_city = 'SEATTLE' 
GROUP BY orderdate
```

有关语法信息，请参阅[CREATE PROTECTED MULTI DIALECT VIEW](create-view.md#create-protected-multi-dialect-view)。

## 查询 Data Catalog 视图
<a name="views-glue-querying-a-data-catalog-view"></a>

视图创建后，`Lake Formation` 管理员可以向 `Invoker` 主体授予对 Data Catalog 视图的 `SELECT` 权限。然后，`Invoker` 主体无需访问视图所引用的基础基表，即可查询视图。以下是 `Invoker` 查询示例。

```
SELECT * from orders_by_date where price > 5000
```

## 注意事项和限制
<a name="views-glue-limitations"></a>

以下大多数 Data Catalog 视图限制特定于 Athena。有关同样适用于其他服务的 Data Catalog 视图的其他限制，请参阅 Lake Formation 文档。
+ Data Catalog 视图不能引用其他视图、数据库资源链接或表资源链接。
+ 在视图定义中，您最多可以引用 10 个表。
+ 在 Lake Formation 中，表不得具有 `IAMAllowedPrincipals` 数据湖权限。如果有，则会出现错误“多方言视图只能引用没有 IAMAllowedPrincipals 权限的表”。
+ 该表的 Amazon S3 位置必须注册为 Lake Formation 数据湖位置。如果该表未按此方式注册，则会出现错误“多方言视图只能引用 Lake Formation 托管表”。有关如何在 Lake Formation 中注册 Amazon S3 位置的信息，请参阅*AWS Lake Formation开发者指南*中的[注册 Amazon S3 位置](https://docs.aws.amazon.com/lake-formation/latest/dg/register-location.html)。
+ AWS Glue[GetTables](https://docs.aws.amazon.com/glue/latest/webapi/API_GetTables.html) 和 [SearchTables](https://docs.aws.amazon.com/glue/latest/webapi/API_SearchTables.html) API 调用不会更新 `IsRegisteredWithLakeFormation` 参数。要查看参数的正确值，请使用 AWS Glue [GetTable](https://docs.aws.amazon.com/glue/latest/webapi/API_GetTable.html) API。有关更多信息，请参阅*AWS Lake Formation开发者指南*中的 [GetTables 和 SearchTables API 不会更新 IsRegisteredWithLakeFormation 参数的值](https://docs.aws.amazon.com/lake-formation/latest/dg/limitations.html#issue-GetTables-value)。
+ `DEFINER` 主体只能是 IAM 角色。
+ `DEFINER` 角色必须对基础表具有完全的 `SELECT`（可授予）权限。
+ 不支持 `UNPROTECTED` Data Catalog 视图。
+ 视图定义中不支持用户定义的函数（UDF）。
+ Athena 联合数据来源不能用于 Data Catalog 视图。
+ 外部 Hive 元存储不支持 Data Catalog 视图。
+ Athena 在检测到过时视图时会显示错误消息。发生以下一种情况时，会报告过时的视图：
  + 视图引用了不存在的表或数据库。
  + 在引用的表中进行了架构或元数据更改。
  + 删除了引用的表并使用不同的架构或配置重新创建。

## 权限
<a name="views-glue-permissions"></a>

Data Catalog 视图需要三个角色：`Lake Formation Admin`、`Definer` 和 `Invoker`。
+ **`Lake Formation Admin`** – 有权配置所有 Lake Formation 权限。
+ **`Definer`** – 创建 Data Catalog 视图。`Definer` 角色必须对视图定义引用的所有基础表具有完全的可授予 `SELECT` 权限。
+ **`Invoker`** – 可以查询 Data Catalog 视图或检查其元数据。若要显示查询的调用者，可以使用 `invoker_principal()` DML 函数。有关更多信息，请参阅 [invoker\$1principal()](functions-env3.md#functions-env3-invoker-principal)。

`Definer` 角色的信任关系必须允许 AWS Glue 和 Lake Formation 服务主体采取 `sts:AssumeRole` 操作。有关更多信息，请参阅《AWS Lake Formation 开发人员指南》**中的[创建视图的先决条件](https://docs.aws.amazon.com/lake-formation/latest/dg/working-with-views.html#views-prereqs)。

还需要针对 Athena 访问的 IAM 权限。有关更多信息，请参阅 [Amazon Athena 的 AWS 托管策略](security-iam-awsmanpol.md)。

# 管理 Data Catalog 视图
<a name="views-glue-managing"></a>

您可以使用 DDL 命令来更新和管理您的 Data Catalog 视图。

## 更新 Data Catalog 视图
<a name="views-glue-updating-a-data-catalog-view"></a>

`Lake Formation` 管理员或定义者可以使用 `ALTER VIEW UPDATE DIALECT` 语法来更新视图定义。以下示例修改视图定义，从 `returns` 表（而不是 `orders` 表）中选择列。

```
ALTER VIEW orders_by_date UPDATE DIALECT
AS
SELECT return_date, sum(totalprice) AS price
FROM returns
WHERE order_city = 'SEATTLE'
GROUP BY orderdate
```

## AWS Glue Data Catalog 视图支持的 DDL 操作
<a name="views-glue-supported-actions"></a>

Athena 支持 AWS Glue Data Catalog 视图的下列操作。


| 语句 | 说明 | 
| --- | --- | 
| [ALTER VIEW DIALECT](alter-view-dialect.md) |  可以通过添加引擎方言，或者更新或删除现有的引擎方言来更新 Data Catalog 视图。  | 
| [CREATE PROTECTED MULTI DIALECT VIEW](create-view.md#create-protected-multi-dialect-view) |  从指定的 `SELECT` 查询创建 Data Catalog 视图。有关更多信息，请参阅 [CREATE PROTECTED MULTI DIALECT VIEW](create-view.md#create-protected-multi-dialect-view)。 可选的 `OR REPLACE` 子句允许您通过替换来更新现有视图。  | 
| [DESCRIBE VIEW](describe-view.md) |  显示命名视图的列列表。这能让您检查复杂视图的属性。  | 
| [DROP VIEW](drop-view.md) |  删除现有视图。如果该视图不存在，可选 `IF EXISTS` 子句将抑制错误出现。  | 
| [SHOW CREATE VIEW](show-create-view.md) |  显示创建指定视图的 SQL 语句。  | 
| [SHOW VIEWS](show-views.md) |  列出指定数据库或当前数据库（如果省略数据库的名称）中的视图。将可选 `LIKE` 子句与一个正则表达式结合使用来限制视图名称列表。您还可以在控制台的左窗格中看到视图列表。  | 
| [SHOW COLUMNS](show-columns.md) |  列出视图的架构中的列。  | 

# 使用已保存的查询
<a name="saved-queries"></a>

您可以使用 Athena 控制台保存、编辑、运行、重命名和删除您在查询编辑器中创建的查询。

## 注意事项和限制
<a name="saved-queries-considerations-and-limitations"></a>
+ 您可以更新已保存的查询的名称、描述和查询文本。
+ 您只能更新自己账户中的查询。
+ 您无法更改查询所属的工作组或数据库。
+ Athena 不会保存查询的修订历史记录。如果您需要保留特定版本的查询，请将其保存为其他名称。

**注意**  
现在可以在 Amazon SageMaker 融通式合作开发工作室（预览版）中访问 Amazon Athena，它可以帮助您访问组织的数据，并使用最佳工具对其采取措施。您可以将保存的查询从 Athena 工作组迁移到 SageMaker 融通式合作开发工作室，使用现有 Athena 工作组配置项目，并通过 IAM 角色更新来维护必要的权限。有关更多信息，请参阅[将 Amazon Athena 资源迁移到 Amazon SageMaker 融通式合作开发工作室（预览版）](https://github.com/aws/Unified-Studio-for-Amazon-Sagemaker/tree/main/migration/athena)。

**Topics**
+ [注意事项和限制](#saved-queries-considerations-and-limitations)
+ [使用名称保存查询](saved-queries-name.md)
+ [运行已保存的查询](saved-queries-run.md)
+ [编辑已保存的查询](saved-queries-edit.md)
+ [重命名或删除已保存查询](saved-queries-rename-or-delete.md)
+ [重命名未显示的已保存查询](saved-queries-rename-not-displayed.md)
+ [删除未显示的已保存查询](saved-queries-delete-not-displayed.md)
+ [使用 Athena API 更新已保存的查询](saved-queries-update-with-api.md)

# 使用名称保存查询
<a name="saved-queries-name"></a>

**保存查询并为其命名**

1. 在 Athena 控制台查询编辑器中输入或运行一个查询。

1. 在查询编辑器窗口上方的查询选项卡上，选择三个竖直点，然后选择 **Save as**（另存为）。

1. 在 **Save query**（保存查询）对话框中，输入查询的名称和可选的描述。您可以使用可展开的 **Preview SQL query**（预览 SQL 查询）窗口，以在保存查询之前验证查询的内容。

1. 选择 **Save query**（保存查询）。

   在查询编辑器中，该查询的选项卡将显示您指定的名称。

# 运行已保存的查询
<a name="saved-queries-run"></a>

**运行保存的查询**

1. 在 Athena 控制台中，选择 **Saved queries**（已保存的查询）选项卡。

1. 在 **Saved queries**（已保存的查询）列表中，选择要运行的查询 ID。

   查询编辑器将会显示您选择的查询。

1. 选择**运行**。

# 编辑已保存的查询
<a name="saved-queries-edit"></a>

**编辑已保存的查询**

1. 在 Athena 控制台中，选择 **Saved queries**（已保存的查询）选项卡。

1. 在 **Saved queries**（已保存的查询）列表中，选择要编辑的查询 ID。

1. 在查询编辑器中编辑查询。

1. 执行下列步骤之一：
   + 要运行查询，请选择 **Run**（运行）。
   + 要保存查询，请在查询选项卡上选择三个竖直点，然后选择 **Save**（保存）。
   + 要使用其他名称保存查询，请在查询选项卡上选择三个竖直点，然后选择 **Save as**（另存为）。

# 重命名或删除已保存查询
<a name="saved-queries-rename-or-delete"></a>

**重命名或删除在查询编辑器中显示的已保存查询**

1. 在查询选项卡上选择三个竖直点，然后选择 **Rename**（重命名）或 **Delete**（删除）。

1. 按照提示重命名或删除查询。

# 重命名未显示的已保存查询
<a name="saved-queries-rename-not-displayed"></a>

**重命名未在查询编辑器中显示的已保存查询**

1. 在 Athena 控制台中，选择 **Saved queries**（已保存的查询）选项卡。

1. 选择要重命名的查询对应的复选框。

1. 选择**重命名**。

1. 在 **Rename query**（重命名查询）对话框中，编辑查询名称和查询描述。您可以使用可展开的 **Preview SQL query**（预览 SQL 查询）窗口，以在重命名查询之前验证查询的内容。

1. 选择 **Rename query**（重命名查询）。

   重命名的查询将在 **Saved queries**（已保存的查询）列表中显示。

# 删除未显示的已保存查询
<a name="saved-queries-delete-not-displayed"></a>

**删除未在查询编辑器中显示的已保存查询**

1. 在 Athena 控制台中，选择 **Saved queries**（已保存的查询）选项卡。

1. 选择要删除的一个或多个查询对应的复选框。

1. 选择**删除**。

1. 出现确认提示时，选择 **Delete**（删除）。

   一个或多个查询将从 **Saved queries**（已保存的查询）列表中删除。

# 使用 Athena API 更新已保存的查询
<a name="saved-queries-update-with-api"></a>

有关使用 Athena API 更新已保存的查询的信息，请参阅《Athena API 参考》中的 [UpdateNamedQuery](https://docs.aws.amazon.com/athena/latest/APIReference/API_UpdateNamedQuery.html) 操作。

# 使用参数化查询
<a name="querying-with-prepared-statements"></a>

您可以使用 Athena 参数化查询在执行时使用不同的参数值重新运行同一个查询，从而帮助防止 SQL 注入攻击。在 Athena 中，参数化查询可以在任何 DML 查询或 SQL 预准备语句中采用执行参数的形式。
+ 带有执行参数的查询可以在单个步骤中完成，而不是特定于工作组。您可以在任何 DML 查询中为要参数化的值放置问号。运行查询时，按顺序声明执行参数的值。参数的声明和参数的赋值可以在同一查询中完成，但单独进行。与预准备语句不同，您可以在提交带有执行参数的查询时选择工作组。
+ 预准备语句需要两个单独的 SQL 语句：`PREPARE` 和 `EXECUTE`。首先，在 `PREPARE` 语句中定义参数。然后，运行为您定义的参数提供值的 `EXECUTE` 语句。预准备语句特定于工作组；您不能在预准备语句所属的工作组的上下文以外运行它们。

## 注意事项和限制
<a name="querying-with-prepared-statements-considerations-and-limitations"></a>
+ 参数化查询仅在 Athena 引擎版本 2 及更高版本中受支持。有关 Athena 引擎版本的更多信息，请参阅 [Athena 引擎版本控制](engine-versions.md)。
+ 目前，参数化查询仅在 `SELECT`、`INSERT INTO`、`CTAS` 和 `UNLOAD` 语句中受支持。
+ 在参数化查询中，参数是位置参数，并用 `?` 指示。参数按其在查询中的顺序赋值。命名参数不受支持。
+ 目前，`?` 参数只能放在 `WHERE` 子句中。不支持 `SELECT ? FROM table` 之类的语法。
+ 问号参数不能放在双引号或单引号以内（即 `'?'` 和 `"?"` 不是有效的语法）。
+ 要将 SQL 执行参数视为字符串，它们必须用单引号而不是双引号括起来。
+ 如有必要，您可以在为参数化术语输入值时使用 `CAST` 函数。例如，如果您有一列 `date` 类型在查询中参数化，并且想要查询日期 `2014-07-05`，则为参数值输入 `CAST('2014-07-05' AS DATE)` 将返回结果。
+ 预准备语句特定于工作组，预准备语句名称在工作组中必须唯一。
+ 预准备语句的 IAM 权限是必需的。有关更多信息，请参阅 [配置对于预编译语句的访问](security-iam-athena-prepared-statements.md)。
+ 在 Athena 控制台中使用执行参数的查询最多只能有 25 个问号。

**Topics**
+ [注意事项和限制](#querying-with-prepared-statements-considerations-and-limitations)
+ [使用执行参数](querying-with-prepared-statements-querying-using-execution-parameters.md)
+ [使用预准备语句](querying-with-prepared-statements-querying.md)
+ [其他资源](querying-with-prepared-statements-additional-resources.md)

# 使用执行参数
<a name="querying-with-prepared-statements-querying-using-execution-parameters"></a>

您可以在任何 DML 查询中使用问号占位符创建参数化查询，而无需先创建预准备语句。要运行这些查询，您可以使用 Athena 控制台，或者使用 AWS CLI 或 AWS SDK，并在 `execution-parameters` 参数中声明变量。

**Topics**
+ [使用 Athena 控制台](querying-with-prepared-statements-running-queries-with-execution-parameters-in-the-athena-console.md)
+ [使用 AWS CLI](querying-with-prepared-statements-running-queries-with-execution-parameters-using-the-aws-cli.md)

# 在 Athena 控制台中运行具有执行参数的查询
<a name="querying-with-prepared-statements-running-queries-with-execution-parameters-in-the-athena-console"></a>

在 Athena 控制台中运行具有执行参数（问号）的参数化查询时，系统会按照问号在查询中出现的顺序提示您输入值。

**要运行具有执行参数的查询**

1. 请在 Athena 编辑器中输入带有问号占位符的查询，如以下示例所示。

   ```
   SELECT * FROM "my_database"."my_table"
   WHERE year = ? and month= ? and day= ?
   ```

1. 选择**运行**。

1. 在 **Enter parameters（输入参数）**对话框中，按查询中每个问号的顺序输入值。  
![\[按顺序输入查询参数的值\]](http://docs.aws.amazon.com/zh_cn/athena/latest/ug/images/querying-with-prepared-statements-1.png)

1. 输入完参数后，选择 **Run（运行）**。编辑器会显示您输入的参数值的查询结果。

在这种情况下，您可以执行下列操作之一：
+ 为同一个查询输入不同的参数值，然后再次选择 **Run（运行）**。
+ 要立即清除输入的所有值，请选择 **Clear（清除）**。
+ 要直接编辑查询（例如，添加或删除问号），请关闭 **Enter parameters（输入参数）**对话框。
+ 要保存参数化查询以供后续使用，请选择 **Save（保存）**或者 **Save as（另存为）**，然后为查询命名。有关使用已保存查询的更多信息，请参阅 [使用已保存的查询](saved-queries.md)。

为方便起见，**Enter parameters（输入参数）**对话框会记住先前为查询输入的值，只要您在查询编辑器中使用同一选项卡。

# 使用 AWS CLI 运行具有执行参数的查询
<a name="querying-with-prepared-statements-running-queries-with-execution-parameters-using-the-aws-cli"></a>

要使用 AWS CLI 运行具有执行参数的查询，请使用 `start-query-execution` 命令并在 `query-string` 参数中提供参数化查询。然后，在 `execution-parameters` 参数中，提供执行参数的值。以下示例对此方法进行了说明。

```
aws athena start-query-execution 
--query-string "SELECT * FROM table WHERE x = ? AND y = ?"
--query-execution-context "Database"="default" 
--result-configuration "OutputLocation"="s3://amzn-s3-demo-bucket;/..."
--execution-parameters "1" "2"
```

# 使用预准备语句
<a name="querying-with-prepared-statements-querying"></a>

您可以使用预编译语句重复执行具有不同查询参数的同一查询。预准备语句包含参数占位符，其值在执行时提供。

**注意**  
工作组中的最大预处理语句数为 1000。

**Topics**
+ [SQL 语法](querying-with-prepared-statements-sql-statements.md)
+ [使用 Athena 控制台](querying-with-prepared-statements-executing-prepared-statements-without-the-using-clause-athena-console.md)
+ [使用 AWS CLI](querying-with-prepared-statements-cli-section.md)

# 预准备语句的 SQL 语法
<a name="querying-with-prepared-statements-sql-statements"></a>

您可以使用 `PREPARE`、`EXECUTE` 和 `DEALLOCATE PREPARE` SQL 语句在 Athena 控制台查询编辑器中运行参数化查询。

 
+ 要指定通常会使用文字值的参数，请在 `PREPARE` 语句中使用问号。
+ 若要在运行查询时将参数替换为值，请在 `EXECUTE` 语句中使用 `USING` 子句。
+ 要从工作组中的预准备语句中删除预准备语句，请使用 `DEALLOCATE PREPARE` 语句。

以下各部分提供了有关这些语句的其他详细信息。

**Topics**
+ [PREPARE](querying-with-prepared-statements-prepare.md)
+ [EXECUTE](querying-with-prepared-statements-execute.md)
+ [DEALLOCATE PREPARE](querying-with-prepared-statements-deallocate-prepare.md)

# PREPARE
<a name="querying-with-prepared-statements-prepare"></a>

准备要在之后运行的语句。预准备语句将以您指定的名称保存在当前工作组中。语句可以包含参数来代替要在查询运行时替换的文字。要替换为值的参数用问号表示。

## 语法
<a name="querying-with-prepared-statements-prepare-syntax"></a>

```
PREPARE statement_name FROM statement
```

下表介绍了这些参数。


****  

| 参数 | 说明 | 
| --- | --- | 
| statement\$1name | 要执行的预准备语句的名称。此名称在工作组范围内必须唯一。 | 
| 语句 | SELECT、CTAS 或 INSERT INTO 查询。 | 

## PREPARE 示例
<a name="querying-with-prepared-statements-prepare-examples"></a>

以下示例将演示如何使用 `PREPARE` 语句。问号表示在运行查询时由 `EXECUTE` 语句提供的值。

```
PREPARE my_select1 FROM
SELECT * FROM nation
```

```
PREPARE my_select2 FROM
SELECT * FROM "my_database"."my_table" WHERE year = ?
```

```
PREPARE my_select3 FROM
SELECT order FROM orders WHERE productid = ? and quantity < ?
```

```
PREPARE my_insert FROM
INSERT INTO cities_usa (city, state)
SELECT city, state
FROM cities_world
WHERE country = ?
```

```
PREPARE my_unload FROM
UNLOAD (SELECT * FROM table1 WHERE productid < ?)
TO 's3://amzn-s3-demo-bucket/'
WITH (format='PARQUET')
```

# EXECUTE
<a name="querying-with-prepared-statements-execute"></a>

运行预准备语句。参数的值在 `USING` 子句中指定。

## 语法
<a name="querying-with-prepared-statements-execute-syntax"></a>

```
EXECUTE statement_name [USING value1 [ ,value2, ... ] ]
```

*statement\$1name* 是预准备语句的名称。*value1* 和 *value2* 是要为语句中的参数指定的值。

## EXECUTE 示例
<a name="querying-with-prepared-statements-execute-examples"></a>

以下示例将运行 `my_select1` 预准备语句，该语句不包含任何参数。

```
EXECUTE my_select1
```

以下示例将运行 `my_select2` 预准备语句，其中包含一个参数。

```
EXECUTE my_select2 USING 2012
```

以下示例将运行 `my_select3` 预准备语句，它使用两个参数。

```
EXECUTE my_select3 USING 346078, 12
```

以下示例为预准备语句 `my_insert` 中的参数提供字符串值。

```
EXECUTE my_insert USING 'usa'
```

以下示例为预准备语句 `my_unload` 中的 `productid` 参数提供数字值。

```
EXECUTE my_unload USING 12
```

# DEALLOCATE PREPARE
<a name="querying-with-prepared-statements-deallocate-prepare"></a>

从当前工作组中的预准备语句列表中删除具有指定名称的预准备语句。

## 语法
<a name="querying-with-prepared-statements-deallocate-prepare-syntax"></a>

```
DEALLOCATE PREPARE statement_name
```

*statement\$1name* 是要删除的预准备语句的名称。

## 示例
<a name="querying-with-prepared-statements-deallocate-prepare-examples"></a>

以下示例从当前工作组中删除 `my_select1` 预准备语句。

```
DEALLOCATE PREPARE my_select1
```

# 在 Athena 控制台中运行交互式预准备语句
<a name="querying-with-prepared-statements-executing-prepared-statements-without-the-using-clause-athena-console"></a>

如果在查询编辑器中使用语法 `EXECUTE` *prepared\$1stament* 运行现有预准备语句，Athena 会打开 **Enter parameters（输入参数）**对话框，这样您就可以输入通常出现在 `EXECUTE ... USING` 语句的 `USING` 子句中的值。

**使用 **Enter parameters（输入参数）**对话框运行预准备语句**

1. 在查询编辑器中，使用语法 `EXECUTE` *prepared\$1stament* 而不是 `EXECUTE prepared_statement USING` *Value1* `,` *Value2* ` ...`。

1. 选择**运行**。将显示 **Enter parameters（输入参数）**对话框。  
![\[在 Athena 控制台中输入预准备语句的参数值。\]](http://docs.aws.amazon.com/zh_cn/athena/latest/ug/images/querying-with-prepared-statements-2.png)

1. 按照 **Execution parameters（执行参数）**对话框中的顺序输入值。由于查询的原始文本不可见，因此必须记住每个位置参数的含义或使预准备语句可供参考。

1. 选择**运行**。

# 使用 AWS CLI 创建、执行和列出预准备语句
<a name="querying-with-prepared-statements-cli-section"></a>

您可以使用 AWS CLI 创建、执行和列出预准备语句。

**Topics**
+ [创建](querying-with-prepared-statements-creating-prepared-statements-using-the-aws-cli.md)
+ [Execute](querying-with-prepared-statements-cli-executing-prepared-statements.md)
+ [列表](querying-with-prepared-statements-listing.md)

# 使用 AWS CLI 创建预准备语句
<a name="querying-with-prepared-statements-creating-prepared-statements-using-the-aws-cli"></a>

要使用 AWS CLI 创建预准备语句，您可以使用以下 `athena` 命令中的一个：
+ 使用 `create-prepared-statement` 命令并提供包含执行参数的查询语句。
+ 使用 `start-query-execution` 命令并提供使用 `PREPARE` 语法的查询字符串。

## 使用 create-prepared-statement
<a name="querying-with-prepared-statements-cli-using-create-prepared-statement"></a>

在 `create-prepared-statement` 命令中，定义 `query-statement` 参数中的查询文本，如以下示例所示。

```
aws athena create-prepared-statement 
--statement-name PreparedStatement1 
--query-statement "SELECT * FROM table WHERE x = ?" 
--work-group athena-engine-v2
```

## 使用 start-query-execution 和 PREPARE 语法
<a name="querying-with-prepared-statements-cli-using-start-query-execution-and-the-prepare-syntax"></a>

使用 `start-query-execution` 命令。在 `query-string` 参数中放置 `PREPARE` 语句，如以下示例所示：

```
aws athena start-query-execution 
--query-string "PREPARE PreparedStatement1 FROM SELECT * FROM table WHERE x = ?" 
--query-execution-context '{"Database": "default"}' 
--result-configuration '{"OutputLocation": "s3://amzn-s3-demo-bucket/..."}'
```

# 使用 AWS CLI 执行预准备语句
<a name="querying-with-prepared-statements-cli-executing-prepared-statements"></a>

要使用 AWS CLI 执行预准备语句，您可以使用以下方法之一为参数提供值：
+ 使用 `execution-parameters` 参数。
+ 使用 `query-string` 参数中的 `EXECUTE ... USING` SQL 语法。

## 使用 execution-parameters 参数
<a name="querying-with-prepared-statements-cli-using-the-execution-parameters-argument"></a>

在此方法中，您可以使用 `start-query-execution` 命令并提供 `query-string` 参数现有预准备语句的名称。然后，在 `execution-parameters` 参数中，提供执行参数的值。以下示例说明了这一方法：

```
aws athena start-query-execution 
--query-string "Execute PreparedStatement1" 
--query-execution-context "Database"="default" 
--result-configuration "OutputLocation"="s3://amzn-s3-demo-bucket/..."
--execution-parameters "1" "2"
```

## 使用 EXECUTE ... 使用 SQL 语法
<a name="querying-with-prepared-statements-cli-using-the-execute-using-sql-syntax"></a>

要使用 `EXECUTE ... USING` 语法运行现有的预准备语句，您可以使用 `start-query-execution` 命令并将预准备语句的名称和参数值都放在 `query-string` 参数中，如以下示例所示：

```
aws athena start-query-execution 
--query-string "EXECUTE PreparedStatement1 USING 1"
--query-execution-context '{"Database": "default"}' 
--result-configuration '{"OutputLocation": "s3://amzn-s3-demo-bucket/..."}'
```

# 使用 AWS CLI 列出预准备语句
<a name="querying-with-prepared-statements-listing"></a>

要列出特定工作组的预准备语句，可以使用 Athena [list-prepared-statements](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/athena/list-prepared-statements.html) AWS CLI 命令或 [ListPreparedStatements](https://docs.aws.amazon.com/athena/latest/APIReference/API_ListPreparedStatements.html) Athena API 操作。`--work-group` 参数是必需的。

```
aws athena list-prepared-statements --work-group primary
```

# 其他资源
<a name="querying-with-prepared-statements-additional-resources"></a>

请参阅 AWS 大数据博客中的以下相关文章。
+ [使用 Amazon Athena 参数化查询提高可重用性和安全性](https://aws.amazon.com/blogs/big-data/improve-reusability-and-security-using-amazon-athena-parameterized-queries/) 
+ [使用 Amazon Athena 参数化查询将数据作为服务提供](https://aws.amazon.com/blogs/big-data/use-amazon-athena-parameterized-queries-to-provide-data-as-a-service/) 

# 使用成本型优化器
<a name="cost-based-optimizer"></a>

您可以使用 Athena SQL 中的成本型优化器（CBO）功能来优化查询。您可以选择请求 Athena 为 AWS Glue 中的一个表收集表级或列级统计数据。如果查询中的所有表都有统计数据，Athena 会使用这些统计数据来创建它认为性能最佳的执行计划。查询优化器会根据统计模型来计算备选计划，然后选择速度可能最快的计划来运行查询。

AWS Glue 表上的统计数据被收集并存储在 AWS Glue Data Catalog 中，也提供给 Athena 来改进查询计划和执行。这些统计数据是列级统计数据，例如 Parquet、ORC、JSON、ION、CSV 和 XML 等文件类型的不同值、空值、最大值和最小值的个数。Amazon Athena 通过在查询处理中尽早应用最严格的筛选条件来使用这些统计数据优化查询。这种筛选功能限制了内存使用量和为提供查询结果而必须读取的记录数。

Athena 还会将 CBO 和规则型优化器（RBO）功能结合起来使用。RBO 以机械方式应用可提高查询性能的规则。RBO 通常会带来益处，因为它的转换是为了简化查询计划。不过，由于 RBO 不进行成本计算或计划比较，查询较复杂便会让 RBO 难以创建最佳计划。

因此，Athena 会同时使用 RBO 和 CBO 来优化您的查询。在确定改进查询执行的机会后，Athena 就会创建最佳计划。有关执行计划详情的信息，请参阅[查看 SQL 查询的执行计划](query-plans.md)。有关 CBO 如何工作的详细讨论，请参阅 AWS 大数据博客中的 [Speed up queries with the cost-based optimizer in Amazon Athena](https://aws.amazon.com/blogs/big-data/speed-up-queries-with-cost-based-optimizer-in-amazon-athena/)。

要为 AWS Glue Catalog 表生成统计数据，可以使用 Athena 控制台、AWS Glue 控制台或 AWS Glue API。由于 Athena 已与 AWS Glue Catalog 集成，在运行来自 Amazon Athena 的查询时，您会自动获得相应的查询性能改进。

## 注意事项和限制
<a name="cost-based-optimizer-considerations-and-limitations"></a>
+ **表类型** – 目前，Athena 中的 CBO 功能仅支持 AWS Glue Data Catalog 中的 Hive 和 Iceberg 表。
+ **Athena for Spark** – CBO 功能在 Athena for Spark 中不可用。
+ **定价** – 有关定价信息，请访问 [AWS Glue 定价页面](https://aws.amazon.com/glue/pricing)。

## 使用 Athena 控制台生成表统计数据
<a name="cost-based-optimizer-generating-table-statistics-using-the-athena-console"></a>

本节旨在介绍如何使用 Athena 控制台为 AWS Glue 中的表生成表级或列级统计数据。有关使用 AWS Glue 生成表统计数据的信息，请参阅《AWS Glue Developer Guide》**中的 [Working with column statistics](https://docs.aws.amazon.com/glue/latest/dg/column-statistics.html)。

**使用 Athena 控制台为表生成统计数据**

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

1. 在 Athena 查询编辑器的**表**列表中，选择所需表的三个垂直点，然后选择**生成统计数据**。  
![\[Athena 查询编辑器中表的上下文菜单。\]](http://docs.aws.amazon.com/zh_cn/athena/latest/ug/images/cost-based-optimizer-1.png)

1. 在**生成统计数据**对话框中，选择**所有列**为表中的所有列生成统计数据，或者选择**所选列**来选择特定列。**所有列**为默认设置。  
![\[“生成统计数据”对话框。\]](http://docs.aws.amazon.com/zh_cn/athena/latest/ug/images/cost-based-optimizer-2.png)

1. 对于 **AWS Glue 服务角色**，创建服务角色或选择现有服务角色来授予 AWS Glue 生成统计数据的权限。对于包含表数据的 Amazon S3 存储桶，AWS Glue 服务角色还需要 [https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetObject.html](https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetObject.html) 权限。  
![\[选择 AWS Glue 服务角色。\]](http://docs.aws.amazon.com/zh_cn/athena/latest/ug/images/cost-based-optimizer-3.png)

1. 选择**生成统计数据**。**为 *table\$1name* 生成统计数据**通知横幅显示任务状态。  
![\[“生成统计数据”通知横幅。\]](http://docs.aws.amazon.com/zh_cn/athena/latest/ug/images/cost-based-optimizer-4.png)

1. 要在 AWS Glue 控制台中查看详细信息，请选择**在 Glue 中查看**。

   有关在 AWS Glue 控制台中查看统计数据的信息，请参阅《AWS Glue 开发人员指南**》中的 [Viewing column statistics](https://docs.aws.amazon.com/glue/latest/dg/view-column-stats.html)。

1. 生成统计数据后，包含统计数据的表和列在括号中显示**统计数据**一词，如下图所示。  
![\[在 Athena 查询编辑器中显示统计数据图标的表。\]](http://docs.aws.amazon.com/zh_cn/athena/latest/ug/images/cost-based-optimizer-5.png)

现在，如果运行查询，Athena 会对生成统计数据的表和列执行基于成本的优化。

## 启用和禁用表统计数据
<a name="cost-based-optimizer-enabling-iceberg-table-statistics"></a>

按照上一节中的步骤为 Iceberg 表生成表统计数据时，会自动向 AWS Glue Data Catalog 中的 Iceberg 表添加一个称为 `use_iceberg_statistics` 的 Glue 表属性，默认设置为 **true**。如果删除了该属性或将其设置为 **false**，则即使 Glue 生成了统计数据，CBO 在查询执行期间尝试优化查询计划时也不会使用 Iceberg 表统计数据。关于如何生成表统计数据的更多信息，请参阅[使用 Athena 控制台生成表统计数据](#cost-based-optimizer-generating-table-statistics-using-the-athena-console)。

相比之下，Glue 数据目录中的 Hive 表没有启用或禁用 CBO 使用表统计数据的类似表属性。因此，CBO 在尝试优化 Hive 表的查询计划时会始终使用 Glue 生成的表统计数据。

## 其他资源
<a name="cost-based-optimizer-additional-resources"></a>

有关更多信息，请参阅以下资源。

[![AWS Videos](http://img.youtube.com/vi/https://www.youtube.com/embed/zUHEXJdHUxs?si=rMAhJj3I5IlhN-1R/0.jpg)](http://www.youtube.com/watch?v=https://www.youtube.com/embed/zUHEXJdHUxs?si=rMAhJj3I5IlhN-1R)


# 查询 S3 Express One Zone 数据
<a name="querying-express-one-zone"></a>

Amazon S3 Express One Zone 存储类是一种可提供个位数毫秒级响应时间的高性能 Amazon S3 存储类。因此，它非常适合以每秒数十万个请求频繁访问数据的应用程序。

S3 Express One Zone 在同一个可用区内复制和存储数据，优化了速度和成本。这与 Amazon S3 区域存储类不同，后者会自动在一个 AWS 区域 内的至少三个 AWS 可用区之间复制数据。

有关更多信息，请参阅《Amazon S3 用户指南》**中的 [What is S3 Express One Zone?](https://docs.aws.amazon.com/AmazonS3/latest/userguide/s3-express-one-zone.html)。

## 先决条件
<a name="querying-express-one-zone-prerequisites"></a>

在开始使用之前，请确认满足以下条件：
+ **Athena 引擎版本 3** – 要将 S3 Express One Zone 与 Athena SQL 结合使用，必须将您的工作组配置为使用 Athena 引擎版本 3。
+ **S3 Express One Zone 权限** – 当 S3 Express One Zone 在 Amazon S3 对象上调用 `GET`、`LIST` 或 `PUT` 等操作时，存储类会代表您调用 `CreateSession`。因此，您的 IAM policy 必须允许 `s3express:CreateSession` 操作，这样才能允许 Athena 调用相应的 API 操作。

## 注意事项和限制
<a name="querying-express-one-zone-considerations-and-limitations"></a>

使用 Athena 查询 S3 Express One Zone 时，请注意以下几点。
+ S3 Express One Zone 存储桶支持 `SSE_S3` 和 `SSE-KMS` 加密。无论您在工作组设置中选择哪个选项来加密查询结果，Athena 查询结果都将使用 `SSE_S3` 加密写入。此限制包括 Athena 向 S3 Express One Zone 桶写入数据的所有场景，包括 `CREATE TABLE AS`（CTAS）和 `INSERT INTO` 语句。
+ 不支持使用 AWS Glue 爬网程序在 S3 Express One Zone 数据上创建表。
+ 不支持 `MSCK REPAIR TABLE` 语句。一个解决方法是使用 [ALTER TABLE ADD PARTITION](alter-table-add-partition.md)。
+ S3 Express One Zone 不支持 Apache Iceberg 的表修改 DDL 语句（即，没有 `ALTER TABLE` 语句）。
+ S3 Express One Zone 存储桶不支持 Lake Formation。
+ 不支持以下文件和表格格式或支持受限。如果格式未列出，但受 Athena 支持（例如 Parquet、ORC 和 JSON），则 S3 Express One Zone 存储也支持使用这些格式。  
****    
[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/athena/latest/ug/querying-express-one-zone.html)

## 开始使用
<a name="querying-express-one-zone-getting-started"></a>

使用 Athena 查询 S3 Express One Zone 数据非常简单。要开始使用，请按照以下过程操作。

**使用 Athena SQL 查询 S3 Express One Zone 数据**

1. 将数据转移到 S3 Express One Zone 存储。有关更多信息，请参阅《Amazon S3 用户指南》**中的[设置对象的存储类](https://docs.aws.amazon.com/AmazonS3/latest/userguide/storage-class-intro.html#sc-howtoset)。

1. 在 Athena 中使用 [CREATE TABLE](create-table.md) 语句将数据编入 AWS Glue Data Catalog 目录。有关在 Athena 中创建表的更多信息，请参阅 [在 Athena 中创建表](creating-tables.md) 和 [CREATE TABLE](create-table.md) 语句。

1. （可选）将 Athena 工作组的查询结果位置配置为使用 Amazon S3 *目录桶*。与普通桶相比，Amazon S3 目录桶的性能更高，专为需要一致的个位数毫秒延迟的工作负载或性能至关重要的应用程序而设计。有关更多信息，请参阅《Amazon S3 用户指南》**中的 [Directory buckets overview](https://docs.aws.amazon.com/AmazonS3/latest/userguide/directory-buckets-overview.html)。

# 查询还原的 Amazon Glacier 对象
<a name="querying-glacier"></a>

您可以使用 Amazon Athena 查询从 Amazon Glacier Flexible Retrieval（以前称为 Glacier）和 Amazon Glacier Deep Archive [Amazon S3 存储类别](https://docs.aws.amazon.com/AmazonS3/latest/userguide/storage-class-intro.html#sc-glacier)还原的对象。必须针对每个表启用此功能。如果您在运行查询之前未在表上启用该功能，Athena 将在查询执行期间跳过该表的所有 Amazon Glacier Flexible Retrieval 和 Amazon Glacier Deep Archive 对象。

## 注意事项和限制
<a name="querying-glacier-considerations-and-limitations"></a>
+  只有 Athena 引擎版本 3 支持查询还原的 Amazon Glacier 对象。
+  只有 Apache Hive 表支持该功能。
+  在查询数据之前，您必须还原对象；Athena 不会为您还原对象。

## 将表配置为使用还原的对象
<a name="querying-glacier-configuring-a-table-to-use-restored-objects"></a>

 要将 Athena 表配置为在查询中包含还原的对象，必须将其 `read_restored_glacier_objects` 表属性设置为 `true`。为此，您可以使用 Athena 查询编辑器或 AWS Glue 控制台。您还可以使用 [AWS Glue CLI](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/glue/update-table.html)、[AWS Glue API](https://docs.aws.amazon.com/glue/latest/dg/aws-glue-api-catalog-tables.html#aws-glue-api-catalog-tables-UpdateTable) 或 [AWS Glue SDK](https://docs.aws.amazon.com/glue/latest/dg/sdk-general-information-section.html)。

### 使用 Athena 查询编辑器
<a name="querying-glacier-using-the-athena-query-editor"></a>

 在 Athena 中，您可以使用 [ALTER TABLE SET TBLPROPERTIES](alter-table-set-tblproperties.md) 命令来设置表属性，如下例所示。

```
ALTER TABLE table_name SET TBLPROPERTIES ('read_restored_glacier_objects' = 'true')
```

### 使用 AWS Glue 控制台
<a name="querying-glacier-using-the-aws-glue-console"></a>

 在 AWS Glue 控制台中，执行以下步骤添加 `read_restored_glacier_objects` 表属性。

**在 AWS Glue 控制台中配置表属性**

1. 登录 AWS 管理控制台，然后打开 AWS Glue 控制台，网址为：[https://console.aws.amazon.com/glue/](https://console.aws.amazon.com/glue/)。

1. 请执行以下操作之一：
   + 选择**转到数据目录**。
   + 在导航窗格中，选择**数据目录表**。

1. 在**表**页面的表列表中\$1，选择要编辑的表的链接。

1. 依次选择 **Actions**（操作）、**Edit table**（编辑表）。

1. 在**编辑表**页面的**表属性**部分中，添加以下键值对：
   + 对于 **Key (键)**，添加 `read_restored_glacier_objects`。
   + 对于 **Value**（值），输入 `true`。

1. 选择**保存**。

### 使用 AWS CLI
<a name="querying-glacier-using-the-aws-cli"></a>

 在 AWS CLI 中，您可以使用 AWS Glue [update-table](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/glue/update-table.html) 命令及其 `--table-input` 参数来重新定义表，并在此过程中添加 `read_restored_glacier_objects` 属性。在 `--table-input` 参数中，使用 `Parameters` 结构来指定 `read_restored_glacier_objects` 属性和 `true` 值。请注意：`--table-input` 的参数不能有空格，并且必须使用反斜杠来转义双引号。在以下示例中，将 *my\$1database* 和 *my\$1table* 替换为数据库和表的名称。

```
aws glue update-table \
   --database-name my_database \
   --table-input={\"Name\":\"my_table\",\"Parameters\":{\"read_restored_glacier_objects\":\"true\"}}
```

**重要**  
AWS Glue `update-table` 命令在覆盖模式下运行，这意味着其将用 `table-input` 参数指定的新定义替换现有的表定义。因此，在添加 `read_restored_glacier_objects` 属性时，请务必在 `table-input` 参数中指定希望表中包含的所有字段。

# 处理架构更新
<a name="handling-schema-updates-chapter"></a>

本节提供了有关为各种数据格式处理架构更新的指导。Athena 是一种基于读取的查询引擎。这意味着，当您在 Athena 中创建一个表时，它会在读取数据时应用架构。它不会改变或者重写底层数据。

如果您期望表架构有所变化，则在创建表架构时应考虑采用一种适合您的需求的数据格式。您的目标是对于不断演进的架构仍能再利用现有的 Athena 查询，同时避免在查询包含分区的表时发生架构不匹配错误。

为实现这些目标，请基于下列主题中的表选择表的数据格式。

**Topics**
+ [支持按数据格式划分的架构更新操作](#summary-of-updates)
+ [了解 Apache ORC 和 Apache Parquet 的索引访问权限](#index-access)
+ [更新架构](make-schema-updates.md)
+ [更新包含分区的表](updates-and-partitions.md)

## 支持按数据格式划分的架构更新操作
<a name="summary-of-updates"></a>

下表总结了数据存储格式及其支持的架构处理方式。使用此表有助于您选择格式，即使架构随时间发生了变化，也可继续使用 Athena 查询。

在此表中，您可以观察到 Parquet 和 ORC 的列式格式具有不同的默认列访问方法。预设情况下，Parquet 将按名称访问列，ORC 按索引（序数值）访问列。因此，Athena 可在创建表时定义一个 SerDe 属性，以切换默认列访问方法，从而在架构演进的过程中实现更大的灵活度。

对于 Parquet，可将 `parquet.column.index.access` 属性设为 `true`，这样可将列访问方法设置为使用列的序号。将此属性设为 `false` 会将列访问方法改为使用列名称。同样，ORC 使用 `orc.column.index.access` 属性控制列访问方法。有关更多信息，请参阅 [了解 Apache ORC 和 Apache Parquet 的索引访问权限](#index-access)。

除了将列重新排序或在表的开头添加列之外，可通过 CSV 和 TSV 进行所有其他架构操作。例如，如果架构演进只需要将列重新命名而不需要删除它们，您可以选择创建 CSV 或 TSV 格式的表。如果您需要删除列，请不要使用 CSV 或 TSV，而要使用任何其他支持的格式，最好是列式格式（例如 Parquet 或 ORC）。


**Athena 中的架构更新和数据格式**  

| 架构更新的预期类型 | 摘要 | CSV（带或不带标头）和 TSV | JSON | AVRO | PARQUET：按名称读取（默认） | PARQUET：按索引读取 | ORC：按索引读取（默认） | ORC：按名称读取 | 
| --- | --- | --- | --- | --- | --- | --- | --- | --- | 
|  [重命名列](updates-renaming-columns.md) | 将您的数据存储为 CSV 和 TSV；或存储为 ORC 和 Parquet（如果按索引读取）。 | Y | N | N | N  | Y | Y | N | 
|  [在表的开头或中间添加新列](updates-add-columns-beginning-middle-of-table.md) | 将您的数据存储为 JSON、AVRO；或存储为 Parquet 和 ORC（如果按名称读取）。不要使用 CSV 和 TSV。 | N | Y | Y | Y | N | N | Y | 
|  [在表的末尾添加列](updates-add-columns-end-of-table.md) | 将您的数据以 CSV 或 TSV、JSON、AVRO、ORC 或 Parquet 格式存储。 | Y | Y | Y | Y | Y | Y | Y | 
| [删除列](updates-removing-columns.md) |  将您的数据存储为 JSON、AVRO；或存储为 Parquet 和 ORC（如果按名称读取）。不要使用 CSV 和 TSV。 | N | Y | Y | Y | N | N | Y | 
| [对列重新排序](updates-reordering-columns.md) | 将您的数据存储为 AVRO、JSON；或存储为 ORC 和 Parquet（如果按名称读取）。 | N | Y | Y | Y | N | N | Y | 
| [更改列的数据类型](updates-changing-column-type.md) | 将您的数据存储为任何格式，但请在 Athena 中测试您的查询，以确保数据类型兼容。对于 Parquet 和 ORC，更改数据类型仅适用于分区表。 | Y | Y | Y | Y | Y | Y | Y | 

## 了解 Apache ORC 和 Apache Parquet 的索引访问权限
<a name="index-access"></a>

PARQUET 和 ORC 是列式数据存储格式，可以按索引或名称读取。将数据存储为这两种格式之一，可在执行所有架构操作和运行 Athena 查询时确保不会产生架构不匹配的错误。
+ Athena *预设情况下按索引读取 ORC*，如 `SERDEPROPERTIES ( 'orc.column.index.access'='true')` 中所定义。有关更多信息，请参阅 [ORC：按索引读取](#orc-read-by-index)。
+ Athena *预设情况下按名称读取 Parquet*，在 `SERDEPROPERTIES ( 'parquet.column.index.access'='false')` 中定义。有关更多信息，请参阅 [Parquet：按名称读取](#parquet-read-by-name)。

这些是默认设置，因此在您的 `CREATE TABLE` 查询中指定这些 SerDe 属性是可选操作，它们是隐式使用的。如果使用这些属性，则允许您运行一些架构更新操作，同时防止其他类似操作。要支持这些操作，请运行另一 `CREATE TABLE` 查询并更改 SerDe 设置。

**注意**  
这些 SerDe 属性*不会*自动传播到每个分区。使用 `ALTER TABLE ADD PARTITION` 语句为每个分区设置 SerDe 属性。要自动执行该流程，请编写一个运行 `ALTER TABLE ADD PARTITION` 语句的脚本。

以下各部分详细描述了这些情况。

### ORC：按索引读取
<a name="orc-read-by-index"></a>

预设情况下，*ORC 格式的表是按索引读取的*。这是由以下语法定义的：

```
WITH SERDEPROPERTIES ( 
  'orc.column.index.access'='true')
```

*按索引读取*允许您将列重新命名。但这样的设置无法删除列或在表的中间添加列。

如果按名称读取 ORC，您就可以在 ORC 表的中间添加列，或删除列，请在 `CREATE TABLE` 语句中将 SerDe 属性 `orc.column.index.access` 设为 `false`。如使用此配置，将无法将列重新命名。

**注意**  
在 Athena 引擎版本 2 中，当 ORC 表设置为按名称读取时，Athena 要求 ORC 文件中的所有列名称均为小写。由于 Apache Spark 在生成 ORC 文件时不会使用小写字段名称，因此 Athena 可能无法读取如此生成的数据。解决方法是使用小写对列重命名，或使用 Athena 引擎版本 3。

以下示例演示了如何将 ORC 更改为按名称读取：

```
CREATE EXTERNAL TABLE orders_orc_read_by_name (
   `o_comment` string,
   `o_orderkey` int, 
   `o_custkey` int, 
   `o_orderpriority` string, 
   `o_orderstatus` string, 
   `o_clerk` string, 
   `o_shippriority` int, 
   `o_orderdate` string
) 
ROW FORMAT SERDE 
  'org.apache.hadoop.hive.ql.io.orc.OrcSerde' 
WITH SERDEPROPERTIES ( 
  'orc.column.index.access'='false') 
STORED AS INPUTFORMAT 
  'org.apache.hadoop.hive.ql.io.orc.OrcInputFormat' 
OUTPUTFORMAT 
  'org.apache.hadoop.hive.ql.io.orc.OrcOutputFormat'
LOCATION 's3://amzn-s3-demo-bucket/orders_orc/';
```

### Parquet：按名称读取
<a name="parquet-read-by-name"></a>

预设情况下，*Parquet 格式的表是按名称读取的*。这是由以下语法定义的：

```
WITH SERDEPROPERTIES ( 
  'parquet.column.index.access'='false')
```

*按名称读取*允许您在表中间添加列或删除列。但该设置无法将列重新命名。

要将 Parquet 设为按索引读取，使您可以将列重新命名，则必须在创建表时将 `parquet.column.index.access` SerDe 属性设为 `true`。

# 更新架构
<a name="make-schema-updates"></a>

本主题介绍了无需实际更改数据即可在 `CREATE TABLE` 语句中对架构进行的一些更改。要更新架构，在某些情况下可以使用 `ALTER TABLE` 命令；而在其他情况下，实际上并不需要修改现有表。而是使用新名称创建一个表，该表修改了在原始 `CREATE TABLE` 语句中使用的架构。

根据您期望架构的演进方式，选择一种兼容的数据格式，以继续使用 Athena 查询。

考察一个应用程序，该应用程序从 `orders` 表中读取订单信息，而该表存在两种格式：CSV 和 Parquet。

以下示例用 Parquet 格式创建一个表：

```
CREATE EXTERNAL TABLE orders_parquet (
   `orderkey` int, 
   `orderstatus` string, 
   `totalprice` double, 
   `orderdate` string, 
   `orderpriority` string, 
   `clerk` string, 
   `shippriority` int
) STORED AS PARQUET
LOCATION 's3://amzn-s3-demo-bucket/orders_ parquet/';
```

以下示例用 CSV 格式创建同样的表：

```
CREATE EXTERNAL TABLE orders_csv (
   `orderkey` int, 
   `orderstatus` string, 
   `totalprice` double, 
   `orderdate` string, 
   `orderpriority` string, 
   `clerk` string, 
   `shippriority` int
) 
ROW FORMAT DELIMITED FIELDS TERMINATED BY ','
LOCATION 's3://amzn-s3-demo-bucket/orders_csv/';
```

以下主题将介绍这些表的更新如何影响 Athena 查询。

**Topics**
+ [在表的开头或中间添加新列](updates-add-columns-beginning-middle-of-table.md)
+ [在表的末尾添加列](updates-add-columns-end-of-table.md)
+ [删除列](updates-removing-columns.md)
+ [重命名列](updates-renaming-columns.md)
+ [对列重新排序](updates-reordering-columns.md)
+ [更改列数据类型](updates-changing-column-type.md)

# 在表的开头或中间添加新列
<a name="updates-add-columns-beginning-middle-of-table"></a>

添加列是最常见的架构变化之一。例如，您可以添加新列，用新的数据来充实表。或者，如果一个现有列的源发生了变化，您可以添加一个新列，同时保留该列表的前一版本，以调整依赖它们的应用程序。

要将列添加到表的开头或中间，并继续针对现有表运行查询，请使用 AVRO、JSON 以及 Parquet 和 ORC（如果它们的 SerDe 属性设为按名称读取）。有关信息，请参阅[了解 Apache ORC 和 Apache Parquet 的索引访问权限](handling-schema-updates-chapter.md#index-access)。

不要在 CSV 和 TSV 表的开头或中间添加列，因为这些格式取决于排序。如果在这些情况下添加列，分区架构改变将导致架构不匹配的错误。

 以下示例创建了一个新表，该表基于 JSON 数据在表的中间添加了一个 `o_comment` 列。

```
CREATE EXTERNAL TABLE orders_json_column_addition (
   `o_orderkey` int, 
   `o_custkey` int, 
   `o_orderstatus` string, 
   `o_comment` string, 
   `o_totalprice` double, 
   `o_orderdate` string, 
   `o_orderpriority` string, 
   `o_clerk` string, 
   `o_shippriority` int, 
) 
ROW FORMAT SERDE 'org.openx.data.jsonserde.JsonSerDe'
LOCATION 's3://amzn-s3-demo-bucket/orders_json/';
```

# 在表的末尾添加列
<a name="updates-add-columns-end-of-table"></a>

如果您使用 Athena 支持的任意格式（例如 Parquet、ORC、Avro、JSON、CSV 和 TSV）创建表，则可使用 `ALTER TABLE ADD COLUMNS` 语句在现有列之后但在分区列之前添加列。

以下示例在 `orders_parquet` 表末尾的任何分区列之前添加一个 `comment` 列：

```
ALTER TABLE orders_parquet ADD COLUMNS (comment string)
```

**注意**  
要在运行 `ALTER TABLE ADD COLUMNS` 后在 Athena 查询编辑器中查看新的表列，请手动刷新编辑器中的表列表，然后重新展开表。

# 删除列
<a name="updates-removing-columns"></a>

如果表中的列不再包含数据，您可能需要删除它们，或者，您可能需要限制对于列数据的访问。
+ 您可以从 JSON、Avro 表，以及按名称读取的 Parquet 和 ORC 表中删除列。有关信息，请参阅[了解 Apache ORC 和 Apache Parquet 的索引访问权限](handling-schema-updates-chapter.md#index-access)。
+ 如果您希望保留已在 Athena 中创建的表，我们不建议从 CSV 和 TSV 表中删除列。删除列会破坏架构，需要您重新创建不包含已删除列的表。

在本示例中，将从 Parquet 表中删除一列``totalprice``并运行查询。在 Athena 中，Parquet 默认是按名称读取的，因此我们省略了指定按名称读取的 SERDEPROPERTIES 配置。请注意，即使更改了架构，以下查询也会成功：

```
CREATE EXTERNAL TABLE orders_parquet_column_removed (
   `o_orderkey` int, 
   `o_custkey` int, 
   `o_orderstatus` string, 
   `o_orderdate` string, 
   `o_orderpriority` string, 
   `o_clerk` string, 
   `o_shippriority` int, 
   `o_comment` string
) 
STORED AS PARQUET
LOCATION 's3://amzn-s3-demo-bucket/orders_parquet/';
```

# 重命名列
<a name="updates-renaming-columns"></a>

有时，您可能需要重命名表列，以便纠正拼写、让列名称含义更清晰或重用现有列以避免列重新排序。

如果将数据存储为 CSV 和 TSV，或 Parquet 和 ORC（配置为按索引读取），则可以将列重新命名。有关信息，请参阅[了解 Apache ORC 和 Apache Parquet 的索引访问权限](handling-schema-updates-chapter.md#index-access)。

Athena 会按照架构中的列顺序读取 CSV 和 TSV 数据并以同样的顺序返回。它不会使用列名称来将数据映射到列，因此，如果不中断 Athena 查询，您就可以重命名 CSV 或 TSV 格式列。

重命名列的一种策略是基于相同的基础数据创建新表，但使用新的列名。以下示例会创建一个名为 `orders_parquet_column_renamed` 新 `orders_parquet` 表。该示例将列 ``o_totalprice`` 名称更改为 ``o_total_price``，然后在 Athena 中运行查询：

```
CREATE EXTERNAL TABLE orders_parquet_column_renamed (
   `o_orderkey` int, 
   `o_custkey` int, 
   `o_orderstatus` string, 
   `o_total_price` double, 
   `o_orderdate` string, 
   `o_orderpriority` string, 
   `o_clerk` string, 
   `o_shippriority` int, 
   `o_comment` string
) 
STORED AS PARQUET
LOCATION 's3://amzn-s3-demo-bucket/orders_parquet/';
```

对于 Parquet 表而言，以下查询可以运行，但经过重命名的列不会显示数据，因为列是按名称访问的（Parquet 的默认设置）而不是按索引访问的：

```
SELECT * 
FROM orders_parquet_column_renamed;
```

对于 CSV 表的查询看起来类似：

```
CREATE EXTERNAL TABLE orders_csv_column_renamed (
   `o_orderkey` int, 
   `o_custkey` int, 
   `o_orderstatus` string, 
   `o_total_price` double, 
   `o_orderdate` string, 
   `o_orderpriority` string, 
   `o_clerk` string, 
   `o_shippriority` int, 
   `o_comment` string
) 
ROW FORMAT DELIMITED FIELDS TERMINATED BY ','
LOCATION 's3://amzn-s3-demo-bucket/orders_csv/';
```

对于 CSV 表而言，以下查询可以运行，并将显示所有列的数据，包括经过重新命名的列：

```
SELECT * 
FROM orders_csv_column_renamed;
```

# 对列重新排序
<a name="updates-reordering-columns"></a>

只有表的数据格式是按名称读取的，才可以将列重新排序，例如 JSON 或默认为按名称读取的 Parquet。如有需要，也可以将 ORC 设为按名称读取。有关信息，请参阅[了解 Apache ORC 和 Apache Parquet 的索引访问权限](handling-schema-updates-chapter.md#index-access)。

以下示例创建了一个新表，其中的列顺序不同：

```
CREATE EXTERNAL TABLE orders_parquet_columns_reordered (
   `o_comment` string,
   `o_orderkey` int, 
   `o_custkey` int, 
   `o_orderpriority` string, 
   `o_orderstatus` string, 
   `o_clerk` string, 
   `o_shippriority` int, 
   `o_orderdate` string
) 
STORED AS PARQUET
LOCATION 's3://amzn-s3-demo-bucket/orders_parquet/';
```

# 更改列数据类型
<a name="updates-changing-column-type"></a>

当现有类型无法再容纳所需的信息量时，您可能需要使用其他列类型。例如，ID 列的值可能超过 `INT` 数据类型的大小，需要使用 `BIGINT` 数据类型。

## 注意事项
<a name="updates-changing-column-type-considerations"></a>

在计划为列使用不同的数据类型时，请考虑以下几点：
+ 在大多数情况下，不能直接更改列的数据类型。而是可以重新创建 Athena 表，并使用新的数据类型定义该列。
+ 只有特定数据类型可以读取为其他数据类型。有关可以进行这种处理的数据类型，请参阅本部分中的表。
+ 对于 Parquet 和 ORC 格式的数据，如果表未进行分区，则将无法为列使用其他数据类型。
+ 对于 Parquet 和 ORC 格式的分区表，一个分区的列类型可以不同于另一个分区的列类型，而且 Athena 将 `CAST` 为所需的类型（如果可能）。有关信息，请参阅[避免带有分区的表出现架构不匹配错误](updates-and-partitions.md#partitions-dealing-with-schema-mismatch-errors)。
+ 对于仅使用 [LazySimpleSerDe](lazy-simple-serde.md) 创建的表，可以使用 `ALTER TABLE REPLACE COLUMNS` 语句将现有列替换为不同的数据类型，但是还必须在语句中重新定义要保留的所有现有列，否则它们将被删除。有关更多信息，请参阅 [ALTER TABLE REPLACE COLUMNS](alter-table-replace-columns.md)。
+ 仅对于 Apache Iceberg 表而言，您可以使用 [ALTER TABLE CHANGE COLUMN](querying-iceberg-alter-table-change-column.md) 语句更改列的数据类型。`ALTER TABLE REPLACE COLUMNS` 不支持 Iceberg 表。有关更多信息，请参阅 [Iceberg 表架构演进](querying-iceberg-evolving-table-schema.md)。

**重要**  
我们强烈建议您在执行数据类型转换时，先测试和验证一下您的查询。如果 Athena 无法使用目标数据类型，则 `CREATE TABLE` 查询可能会失败。

## 使用兼容的数据类型
<a name="updates-changing-column-type-use-compatible-data-types"></a>

尽可能使用兼容的数据类型。下表列出了可被视为其他数据类型的数据类型：


| 原始数据类型 | 可用的目标数据类型 | 
| --- | --- | 
| STRING | BYTE, TINYINT, SMALLINT, INT, BIGINT | 
| BYTE | TINYINT, SMALLINT, INT, BIGINT | 
| TINYINT | SMALLINT, INT, BIGINT | 
| SMALLINT | INT, BIGINT | 
| INT | BIGINT | 
| FLOAT | DOUBLE | 

以下示例为原始 `orders_json` 表使用 `CREATE TABLE` 语句创建一个名为 `orders_json_bigint` 的新表。新表使用 `BIGINT` 而不是 `INT` 作为 ``o_shippriority`` 列的数据类型。

```
CREATE EXTERNAL TABLE orders_json_bigint (
   `o_orderkey` int, 
   `o_custkey` int, 
   `o_orderstatus` string, 
   `o_totalprice` double, 
   `o_orderdate` string, 
   `o_orderpriority` string, 
   `o_clerk` string, 
   `o_shippriority` BIGINT
) 
ROW FORMAT SERDE 'org.openx.data.jsonserde.JsonSerDe'
LOCATION 's3://amzn-s3-demo-bucket/orders_json';
```

以下查询可成功运行，与更改数据类型前的原始 `SELECT` 查询类似：

```
Select * from orders_json 
LIMIT 10;
```

# 更新包含分区的表
<a name="updates-and-partitions"></a>

在 Athena 中，一个表及其分区必须使用相同的数据格式，但它们的架构可以不同。当您创建一个新分区时，该分区通常继承表的架构。随着时间的推移，该架构可能开始变得不同。原因包括：
+ 当您的表的架构发生变化时，分区架构没有更新，从而没有保持与表的架构同步。
+ AWS Glue 爬网程序允许您发现具有不同架构的分区中的数据。这意味着，假如您使用 AWS Glue 在 Athena 中创建了一个表，则当爬网程序完成处理后，该表的架构及其分区可能会有所不同。
+ 如果您直接使用 AWS API 添加分区。

如果分区满足以下约束，Athena 会成功地处理具有分区的表。如果不满足这些约束，Athena 会发出 HIVE\$1PARTITION\$1SCHEMA\$1MISMATCH 错误。
+ 每个分区的架构与表的架构兼容。
+ 表的数据格式允许您要执行的更新类型：添加、删除、重新排序列或更改列的数据类型。

  例如，对于 CSV 和 TSV 格式，您可以将列重命名，在表的最后添加新列，并将列的数据类型更改为其他兼容类型，但您无法删除列。对于其他格式，可以添加或删除列，或者将列的数据类型更改为其他兼容类型。有关信息，请参阅[摘要：Athena 中的更新和数据格式](handling-schema-updates-chapter.md#summary-of-updates)。

## 避免带有分区的表出现架构不匹配错误
<a name="partitions-dealing-with-schema-mismatch-errors"></a>

在查询执行开始时，Athena 通过检查表和分区之间每个列数据类型是否兼容来验证表的架构。
+ 对于 Parquet 和 ORC 数据存储类型，Athena 依赖于列名，并将其用于基于列名的架构验证。这消除了具有 Parquet 和 ORC 格式分区的表的 `HIVE_PARTITION_SCHEMA_MISMATCH` 错误。(如果 SerDe 属性设置为按名称访问索引，则对于 ORC 来说此为真：`orc.column.index.access=FALSE`。Parquet 默认按名称读取索引)。
+ 对于 CSV、JSON 和 Avro，Athena 使用基于索引的架构验证。这意味着，如果您遇到架构不匹配错误，则应删除导致架构不匹配的分区并创新创建它，以便 Athena 可以成功地查询它。

 Athena 会将表的架构与分区架构做比较。假如您使用 AWS Glue 爬网程序在 Athena 中创建了一个 CSV、JSON 和 AVRO 格式的表，则在爬网程序完成处理后，该表的架构和其分区的架构可能会不同。如果表的架构与分区架构之间存在不匹配，则您的 Athena 查询将因类似如下的架构验证错误而失败：'crawler\$1test.click\$1avro' is declared as type 'string', but partition 'partition\$10=2017-01-17' declared column 'col68' as type 'double'. ('crawler\$1test.click\$1avro' 声明为类型 'string'，但分区 'partition\$10=2017-01-17' 声明列 'col68' 的类型为 'double'。)

对于此类错误，通常的解决方法是删除导致错误的分区并重新创建它。有关更多信息，请参阅 [ALTER TABLE DROP PARTITION](alter-table-drop-partition.md) 和 [ALTER TABLE ADD PARTITION](alter-table-add-partition.md)。

# 查询数组
<a name="querying-arrays"></a>

Amazon Athena 允许您创建数组，合并它们，将它们转换为其他数据类型，然后筛选、展平和排序它们。

**Topics**
+ [创建数组](creating-arrays.md)
+ [连接字符串和数组](concatenating-strings-and-arrays.md)
+ [转换数组数据类型](converting-array-data-types.md)
+ [查找数组长度](finding-lengths.md)
+ [访问数组元素](accessing-array-elements.md)
+ [展平嵌套数组](flattening-arrays.md)
+ [从子查询创建数组](creating-arrays-from-subqueries.md)
+ [筛选数组](filtering-arrays.md)
+ [对数组进行排序](sorting-arrays.md)
+ [将聚合函数与数组结合使用](arrays-and-aggregation.md)
+ [将数组转换为字符串](converting-arrays-to-strings.md)
+ [使用数组创建映射](arrays-create-maps.md)
+ [查询具有复杂类型的数组](rows-and-structs.md)

# 创建数组
<a name="creating-arrays"></a>

要在 Athena 中生成数组文本，请使用 `ARRAY` 关键字，后跟方括号 `[ ]`，并包括以逗号分隔的数组元素。

## 示例
<a name="examples"></a>

此查询会创建一个具有四个元素的数组。

```
SELECT ARRAY [1,2,3,4] AS items
```

它返回：

```
+-----------+
| items     |
+-----------+
| [1,2,3,4] |
+-----------+
```

此查询创建两个数组。

```
SELECT ARRAY[ ARRAY[1,2], ARRAY[3,4] ] AS items
```

它返回：

```
+--------------------+
| items              |
+--------------------+
| [[1, 2], [3, 4]]   |
+--------------------+
```

要从兼容类型的选定列创建数组，请如以下示例所示使用查询：

```
WITH
dataset AS (
  SELECT 1 AS x, 2 AS y, 3 AS z
)
SELECT ARRAY [x,y,z] AS items FROM dataset
```

此查询返回：

```
+-----------+
| items     |
+-----------+
| [1,2,3]   |
+-----------+
```

在以下示例中，选定了两个数组，并将其作为欢迎词返回。

```
WITH
dataset AS (
  SELECT
    ARRAY ['hello', 'amazon', 'athena'] AS words,
    ARRAY ['hi', 'alexa'] AS alexa
)
SELECT ARRAY[words, alexa] AS welcome_msg
FROM dataset
```

此查询返回：

```
+----------------------------------------+
| welcome_msg                            |
+----------------------------------------+
| [[hello, amazon, athena], [hi, alexa]] |
+----------------------------------------+
```

要创建一个键值对，请使用 `MAP` 运算符，以便采用一个键数组后跟一个值数组，如以下示例所示：

```
SELECT ARRAY[
   MAP(ARRAY['first', 'last', 'age'],ARRAY['Bob', 'Smith', '40']),
   MAP(ARRAY['first', 'last', 'age'],ARRAY['Jane', 'Doe', '30']),
   MAP(ARRAY['first', 'last', 'age'],ARRAY['Billy', 'Smith', '8'])
] AS people
```

此查询返回：

```
+-----------------------------------------------------------------------------------------------------+
| people                                                                                              |
+-----------------------------------------------------------------------------------------------------+
| [{last=Smith, first=Bob, age=40}, {last=Doe, first=Jane, age=30}, {last=Smith, first=Billy, age=8}] |
+-----------------------------------------------------------------------------------------------------+
```

# 连接字符串和数组
<a name="concatenating-strings-and-arrays"></a>

连接字符串和连接数组使用的是类似的技术。

## 连接字符串
<a name="concatenating-strings"></a>

要连接两个字符串，可以使用双管道 `||` 运算符，如以下示例中所示。

```
SELECT 'This' || ' is' || ' a' || ' test.' AS Concatenated_String
```

此查询返回：


****  

| \$1 | Concatenated\$1String | 
| --- | --- | 
| 1 |  `This is a test.`  | 

您可以使用 `concat()` 函数来获得相同的结果。

```
SELECT concat('This', ' is', ' a', ' test.') AS Concatenated_String
```

此查询返回：


****  

| \$1 | Concatenated\$1String | 
| --- | --- | 
| 1 |  `This is a test.`  | 

您可以使用 `concat_ws()` 函数将字符串与第一个参数中的指定分隔符连接起来。

```
SELECT concat_ws(' ', 'This', 'is', 'a', 'test.') as Concatenated_String
```

此查询返回：


****  

| \$1 | Concatenated\$1String | 
| --- | --- | 
| 1 |  `This is a test.`  | 

要使用点来连接字符串数据类型的两列，请使用双引号引用这两列，并将点括在单引号中作为硬编码字符串。如果某列不是字符串数据类型，则可以先使用 `CAST("column_name" as VARCHAR)` 转换该列。

```
SELECT "col1" || '.' || "col2" as Concatenated_String
FROM my_table
```

此查询返回：


****  

| \$1 | Concatenated\$1String | 
| --- | --- | 
| 1 |  `col1_string_value.col2_string_value`  | 

## 连接数组
<a name="concatenating-arrays"></a>

您可以使用相同的技术来连接数组。

要连接多个数组，请使用双管道 `||` 运算符。

```
SELECT ARRAY [4,5] || ARRAY[ ARRAY[1,2], ARRAY[3,4] ] AS items
```

此查询返回：


****  

| \$1 | 项目 | 
| --- | --- | 
| 1 |  `[[4, 5], [1, 2], [3, 4]]`  | 

要将多个数组组合成一个数组，请使用双管道运算符或 `concat()` 函数。

```
WITH
dataset AS (
  SELECT
    ARRAY ['Hello', 'Amazon', 'Athena'] AS words,
    ARRAY ['Hi', 'Alexa'] AS alexa
)
SELECT concat(words, alexa) AS welcome_msg
FROM dataset
```

此查询返回：


****  

| \$1 | welcome\$1msg | 
| --- | --- | 
| 1 |  `[Hello, Amazon, Athena, Hi, Alexa]`  | 

有关 `concat()` 或其他字符串函数的更多信息，请参阅 Trino 文档中的 [字符串函数和运算符](https://trino.io/docs/current/functions/string.html)。

# 转换数组数据类型
<a name="converting-array-data-types"></a>

要将数组中的数据转换为支持的数据类型，请使用 `CAST` 运算符，例如 `CAST(value AS type)`。Athena 支持所有本机 Presto 数据类型。

```
SELECT
   ARRAY [CAST(4 AS VARCHAR), CAST(5 AS VARCHAR)]
AS items
```

此查询返回：

```
+-------+
| items |
+-------+
| [4,5] |
+-------+
```

创建两个具有键值对元素的数组，将其转换为 JSON，并串联起来，如以下示例所示：

```
SELECT
   ARRAY[CAST(MAP(ARRAY['a1', 'a2', 'a3'], ARRAY[1, 2, 3]) AS JSON)] ||
   ARRAY[CAST(MAP(ARRAY['b1', 'b2', 'b3'], ARRAY[4, 5, 6]) AS JSON)]
AS items
```

此查询返回：

```
+--------------------------------------------------+
| items                                            |
+--------------------------------------------------+
| [{"a1":1,"a2":2,"a3":3}, {"b1":4,"b2":5,"b3":6}] |
+--------------------------------------------------+
```

# 查找数组长度
<a name="finding-lengths"></a>

`cardinality` 函数返回数组的长度，如下例所示：

```
SELECT cardinality(ARRAY[1,2,3,4]) AS item_count
```

此查询返回：

```
+------------+
| item_count |
+------------+
| 4          |
+------------+
```

# 访问数组元素
<a name="accessing-array-elements"></a>

要访问数组元素，请使用 `[]` 运算符，用 1 指定第一个元素，用 2 指定第二个元素，以此类推，如以下示例所示：

```
WITH dataset AS (
SELECT
   ARRAY[CAST(MAP(ARRAY['a1', 'a2', 'a3'], ARRAY[1, 2, 3]) AS JSON)] ||
   ARRAY[CAST(MAP(ARRAY['b1', 'b2', 'b3'], ARRAY[4, 5, 6]) AS JSON)]
AS items )
SELECT items[1] AS item FROM dataset
```

此查询返回：

```
+------------------------+
| item                   |
+------------------------+
| {"a1":1,"a2":2,"a3":3} |
+------------------------+
```

要访问给定位置 (称为索引位置) 的数组元素，请使用 `element_at()` 函数并指定数组名称和索引位置：
+ 如果索引大于 0，`element_at()` 将会返回您指定的元素，从数组的开头计数到末尾。它的行为与 `[]` 运算符一样。
+ 如果索引小于 0，`element_at()` 将会返回元素，从数组的末尾计数到开头。

以下查询将创建一个数组 `words`，并从中选择第一个元素 `hello` 作为 `first_word`，选择第二个元素 `amazon` (从数组末尾计数) 作为 `middle_word`，选择第三个元素 `athena` 作为 `last_word`。

```
WITH dataset AS (
  SELECT ARRAY ['hello', 'amazon', 'athena'] AS words
)
SELECT
  element_at(words, 1) AS first_word,
  element_at(words, -2) AS middle_word,
  element_at(words, cardinality(words)) AS last_word
FROM dataset
```

此查询返回：

```
+----------------------------------------+
| first_word  | middle_word | last_word  |
+----------------------------------------+
| hello       | amazon      | athena     |
+----------------------------------------+
```

# 展平嵌套数组
<a name="flattening-arrays"></a>

当使用嵌套数组时，您通常需要将嵌套数组元素展开到单个阵列中，或将元素展开到多个行中。

## 使用 flatten 函数
<a name="flattening-arrays-flatten-function"></a>

要将嵌套数组的元素展平为单个值数组，请使用 `flatten` 函数。此查询为数组中的每个元素返回一行。

```
SELECT flatten(ARRAY[ ARRAY[1,2], ARRAY[3,4] ]) AS items
```

此查询返回：

```
+-----------+
| items     |
+-----------+
| [1,2,3,4] |
+-----------+
```

## 使用 CROSS JOIN 和 UNNEST
<a name="flattening-arrays-cross-join-and-unnest"></a>

要将数组展平为多个行，请将 `CROSS JOIN` 与 `UNNEST` 运算符结合使用，如以下示例所示：

```
WITH dataset AS (
  SELECT
    'engineering' as department,
    ARRAY['Sharon', 'John', 'Bob', 'Sally'] as users
)
SELECT department, names FROM dataset
CROSS JOIN UNNEST(users) as t(names)
```

此查询返回：

```
+----------------------+
| department  | names  |
+----------------------+
| engineering | Sharon |
+----------------------|
| engineering | John   |
+----------------------|
| engineering | Bob    |
+----------------------|
| engineering | Sally  |
+----------------------+
```

展平一个键值对数组，将选定的键变换到列中，如以下示例所示：

```
WITH
dataset AS (
  SELECT
    'engineering' as department,
     ARRAY[
      MAP(ARRAY['first', 'last', 'age'],ARRAY['Bob', 'Smith', '40']),
      MAP(ARRAY['first', 'last', 'age'],ARRAY['Jane', 'Doe', '30']),
      MAP(ARRAY['first', 'last', 'age'],ARRAY['Billy', 'Smith', '8'])
     ] AS people
  )
SELECT names['first'] AS
 first_name,
 names['last'] AS last_name,
 department FROM dataset
CROSS JOIN UNNEST(people) AS t(names)
```

此查询返回：

```
+--------------------------------------+
| first_name | last_name | department  |
+--------------------------------------+
| Bob        | Smith     | engineering |
| Jane       | Doe       | engineering |
| Billy      | Smith     | engineering |
+--------------------------------------+
```

从员工列表中，选择具有最高组合分数的员工。可以在 `FROM` 子句中使用 `UNNEST`，而无需前面的 `CROSS JOIN`，因为它是默认的联接运算符，因此是隐含的。

```
WITH
dataset AS (
  SELECT ARRAY[
    CAST(ROW('Sally', 'engineering', ARRAY[1,2,3,4]) AS ROW(name VARCHAR, department VARCHAR, scores ARRAY(INTEGER))),
    CAST(ROW('John', 'finance', ARRAY[7,8,9]) AS ROW(name VARCHAR, department VARCHAR, scores ARRAY(INTEGER))),
    CAST(ROW('Amy', 'devops', ARRAY[12,13,14,15]) AS ROW(name VARCHAR, department VARCHAR, scores ARRAY(INTEGER)))
  ] AS users
),
users AS (
 SELECT person, score
 FROM
   dataset,
   UNNEST(dataset.users) AS t(person),
   UNNEST(person.scores) AS t(score)
)
SELECT person.name, person.department, SUM(score) AS total_score FROM users
GROUP BY (person.name, person.department)
ORDER BY (total_score) DESC
LIMIT 1
```

此查询返回：

```
+---------------------------------+
| name | department | total_score |
+---------------------------------+
| Amy  | devops     | 54          |
+---------------------------------+
```

从员工列表中，选择具有最高个人分数的员工。

```
WITH
dataset AS (
 SELECT ARRAY[
   CAST(ROW('Sally', 'engineering', ARRAY[1,2,3,4]) AS ROW(name VARCHAR, department VARCHAR, scores ARRAY(INTEGER))),
   CAST(ROW('John', 'finance', ARRAY[7,8,9]) AS ROW(name VARCHAR, department VARCHAR, scores ARRAY(INTEGER))),
   CAST(ROW('Amy', 'devops', ARRAY[12,13,14,15]) AS ROW(name VARCHAR, department VARCHAR, scores ARRAY(INTEGER)))
 ] AS users
),
users AS (
 SELECT person, score
 FROM
   dataset,
   UNNEST(dataset.users) AS t(person),
   UNNEST(person.scores) AS t(score)
)
SELECT person.name, score FROM users
ORDER BY (score) DESC
LIMIT 1
```

此查询返回：

```
+--------------+
| name | score |
+--------------+
| Amy  | 15    |
+--------------+
```

### CROSS JOIN 和 UNNEST 的注意事项
<a name="flattening-arrays-cross-join-and-unnest-considerations"></a>

如果在查询中的一个或多个数组上使用 `UNNEST`，并且其中一个数组是 `NULL`，则查询不返回任何行。如果在空字符串数组上使用 `UNNEST`，则返回空字符串。

例如，在以下查询中，由于第二个数组为空值，因此查询不返回任何行。

```
SELECT 
    col1, 
    col2 
FROM UNNEST (ARRAY ['apples','oranges','lemons']) AS t(col1)
CROSS JOIN UNNEST (ARRAY []) AS t(col2)
```

在下一个示例中，第二个数组被修改为包含一个空字符串。对于每一行，查询都会返回 `col1` 中的值，并为 `col2` 中的值返回空字符串。要返回第一个数组中的值，需要第二个数组中的空字符串。

```
SELECT 
    col1, 
    col2 
FROM UNNEST (ARRAY ['apples','oranges','lemons']) AS t(col1)
CROSS JOIN UNNEST (ARRAY ['']) AS t(col2)
```

# 从子查询创建数组
<a name="creating-arrays-from-subqueries"></a>

从一组行创建数组。

```
WITH
dataset AS (
  SELECT ARRAY[1,2,3,4,5] AS items
)
SELECT array_agg(i) AS array_items
FROM dataset
CROSS JOIN UNNEST(items) AS t(i)
```

此查询返回：

```
+-----------------+
| array_items     |
+-----------------+
| [1, 2, 3, 4, 5] |
+-----------------+
```

要从一组行创建唯一值的数组，请使用 `distinct` 关键字。

```
WITH
dataset AS (
  SELECT ARRAY [1,2,2,3,3,4,5] AS items
)
SELECT array_agg(distinct i) AS array_items
FROM dataset
CROSS JOIN UNNEST(items) AS t(i)
```

此查询返回以下结果。请注意，不保证排序。

```
+-----------------+
| array_items     |
+-----------------+
| [1, 2, 3, 4, 5] |
+-----------------+
```

有关使用 `array_agg` 函数的更多信息，请参阅 Trino 文档的[聚合函数](https://trino.io/docs/current/functions/aggregate.html)。

# 筛选数组
<a name="filtering-arrays"></a>

如果行集合与筛选条件匹配，则从这些行创建一个数组。

```
WITH
dataset AS (
  SELECT ARRAY[1,2,3,4,5] AS items
)
SELECT array_agg(i) AS array_items
FROM dataset
CROSS JOIN UNNEST(items) AS t(i)
WHERE i > 3
```

此查询返回：

```
+-------------+
| array_items |
+-------------+
| [4, 5]      |
+-------------+
```

根据数组的一个元素是否包含特定值 (例如 2) 来筛选该数组，如以下示例所示：

```
WITH
dataset AS (
  SELECT ARRAY
  [
    ARRAY[1,2,3,4],
    ARRAY[5,6,7,8],
    ARRAY[9,0]
  ] AS items
)
SELECT i AS array_items FROM dataset
CROSS JOIN UNNEST(items) AS t(i)
WHERE contains(i, 2)
```

此查询返回：

```
+--------------+
| array_items  |
+--------------+
| [1, 2, 3, 4] |
+--------------+
```

## 使用 `filter` 函数
<a name="filtering-arrays-filter-function"></a>

```
 filter(ARRAY [list_of_values], boolean_function)
```

您可以使用 `filter` 表达式上的 `ARRAY` 函数来创建新数组，该数组是 *boolean\$1function* 为真的 *list\$1of\$1values* 中项目的子集。`filter` 函数在您无法使用 *UNNEST* 函数时可起到帮助。

以下示例将筛选数组 `[1,0,5,-1]` 中大于零的值。

```
SELECT filter(ARRAY [1,0,5,-1], x -> x>0)
```

**结果**  
`[1,5]`

以下示例将筛选数组 `[-1, NULL, 10, NULL]` 中非空的值。

```
SELECT filter(ARRAY [-1, NULL, 10, NULL], q -> q IS NOT NULL)
```

**结果**  
`[-1,10]`

# 对数组进行排序
<a name="sorting-arrays"></a>

要从一组行创建唯一值的排序数组，您可以使用 [array\$1sort](https://prestodb.io/docs/current/functions/array.html#array_sort) 函数，如以下示例中所示。

```
WITH
dataset AS (
  SELECT ARRAY[3,1,2,5,2,3,6,3,4,5] AS items
)
SELECT array_sort(array_agg(distinct i)) AS array_items
FROM dataset
CROSS JOIN UNNEST(items) AS t(i)
```

此查询返回：

```
+--------------------+
| array_items        |
+--------------------+
| [1, 2, 3, 4, 5, 6] |
+--------------------+
```

要了解如何将数组展开为多行，请参阅 [展平嵌套数组](flattening-arrays.md)。

# 将聚合函数与数组结合使用
<a name="arrays-and-aggregation"></a>
+ 要在数组中添加值，请使用 `SUM`，如以下示例所示。
+ 要在数组中聚合多个行，请使用 `array_agg`。有关信息，请参阅[从子查询创建数组](creating-arrays-from-subqueries.md)。

**注意**  
从 Athena 引擎版本 2 开始的聚合函数支持 `ORDER BY`。

```
WITH
dataset AS (
  SELECT ARRAY
  [
    ARRAY[1,2,3,4],
    ARRAY[5,6,7,8],
    ARRAY[9,0]
  ] AS items
),
item AS (
  SELECT i AS array_items
  FROM dataset, UNNEST(items) AS t(i)
)
SELECT array_items, sum(val) AS total
FROM item, UNNEST(array_items) AS t(val)
GROUP BY array_items;
```

在最后一个 `SELECT` 语句中，您可以使用 `reduce()` 而不是 `sum()` 和 `UNNEST` 来减少处理时间和数据传输，如以下示例所示。

```
WITH
dataset AS (
  SELECT ARRAY
  [
    ARRAY[1,2,3,4],
    ARRAY[5,6,7,8],
    ARRAY[9,0]
  ] AS items
),
item AS (
  SELECT i AS array_items
  FROM dataset, UNNEST(items) AS t(i)
)
SELECT array_items, reduce(array_items, 0 , (s, x) -> s + x, s -> s) AS total
FROM item;
```

任一查询返回以下结果。不保证返回结果的顺序。

```
+----------------------+
| array_items  | total |
+----------------------+
| [1, 2, 3, 4] | 10    |
| [5, 6, 7, 8] | 26    |
| [9, 0]       | 9     |
+----------------------+
```

# 将数组转换为字符串
<a name="converting-arrays-to-strings"></a>

要将数组转换为字符串，请使用 `array_join` 函数。下面的独立示例创建了一个名为的表 `dataset`，它包含一个名为 `words` 的已指定别名数组。该查询使用 `array_join` 将数组元素连接到 `words`，用空格分隔它们，然后将结果字符串返回到名为 `welcome_msg` 的别名列中。

```
WITH
dataset AS (
  SELECT ARRAY ['hello', 'amazon', 'athena'] AS words
)
SELECT array_join(words, ' ') AS welcome_msg
FROM dataset
```

此查询返回：

```
+---------------------+
| welcome_msg         |
+---------------------+
| hello amazon athena |
+---------------------+
```

# 使用数组创建映射
<a name="arrays-create-maps"></a>

映射是由在 Athena 中可用的数据类型组成的键值对。要创建映射，请使用 `MAP` 运算符并将其传递给两个数组：第一个是列 (键) 名称，第二个是值。数组中的所有值都必须具有相同类型。如果有任何映射值数组元素需要具有不同类型，您可以以后转换它们。

## 示例
<a name="examples"></a>

此示例会从数据集中选择用户。它使用 `MAP` 运算符并将其传递给两个数组。第一个数组包含列名称的值，例如“第一个”、“最后一个”和“年龄”。第二个数组包含每个列的值，例如“Bob”、“Smith”、“35”。

```
WITH dataset AS (
  SELECT MAP(
    ARRAY['first', 'last', 'age'],
    ARRAY['Bob', 'Smith', '35']
  ) AS user
)
SELECT user FROM dataset
```

此查询返回：

```
+---------------------------------+
| user                            |
+---------------------------------+
| {last=Smith, first=Bob, age=35} |
+---------------------------------+
```

您可以通过选择字段名称后跟 `[key_name]` 来检索 `Map` 值，如以下示例所示：

```
WITH dataset AS (
 SELECT MAP(
   ARRAY['first', 'last', 'age'],
   ARRAY['Bob', 'Smith', '35']
 ) AS user
)
SELECT user['first'] AS first_name FROM dataset
```

此查询返回：

```
+------------+
| first_name |
+------------+
| Bob        |
+------------+
```

# 查询具有复杂类型和嵌套结构的数组
<a name="rows-and-structs"></a>

您的源数据通常包含具有复杂数据类型和嵌套结构的数组。本部分中的示例显示如何使用 Athena 查询更改元素的数据类型，在数组内找到元素，以及查找关键字。

**Topics**
+ [创建 `ROW`](creating-row.md)
+ [使用 `CAST` 更改数组中的字段名称](changing-row-arrays-with-cast.md)
+ [使用 `.` 表示法筛选数组](filtering-with-dot.md)
+ [筛选具有嵌套值的数组](filtering-nested-with-dot.md)
+ [使用 `UNNEST` 筛选数组](filtering-with-unnest.md)
+ [使用 `regexp_like` 在数组中查找关键字](filtering-with-regexp.md)

# 创建 `ROW`
<a name="creating-row"></a>

**注意**  
本部分中的示例使用 `ROW` 作为一种方法来创建样本数据以供使用。当您在 Athena 中查询表时，无需创建 `ROW` 数据类型，因为它们已从数据源创建。当您使用 `CREATE_TABLE` 时，Athena 将针对数据集中的每一行，在其中定义 `STRUCT`，向其填充数据，并为您创建 `ROW` 数据类型。底层 `ROW` 数据类型包含支持的任何 SQL 数据类型的命名字段。

```
WITH dataset AS (
 SELECT
   ROW('Bob', 38) AS users
 )
SELECT * FROM dataset
```

此查询返回：

```
+-------------------------+
| users                   |
+-------------------------+
| {field0=Bob, field1=38} |
+-------------------------+
```

# 使用 `CAST` 更改数组中的字段名称
<a name="changing-row-arrays-with-cast"></a>

要更改包含 `ROW` 值的数组中的字段名称，您可以对 `ROW` 声明执行 `CAST` 操作：

```
WITH dataset AS (
  SELECT
    CAST(
      ROW('Bob', 38) AS ROW(name VARCHAR, age INTEGER)
    ) AS users
)
SELECT * FROM dataset
```

此查询返回：

```
+--------------------+
| users              |
+--------------------+
| {NAME=Bob, AGE=38} |
+--------------------+
```

**注意**  
在上述示例中，您可以将 `name` 声明为 `VARCHAR`，因为这是它在 Presto 中的类型。如果您在 `CREATE TABLE` 语句中声明此 `STRUCT`，请使用 `String` 类型，因为 Hive 将此数据类型定义为 `String`。

# 使用 `.` 表示法筛选数组
<a name="filtering-with-dot"></a>

在以下示例中，使用点 `.` 表示法从 AWS CloudTrail 日志表的 `userIdentity` 列中选择 `accountId` 字段。有关更多信息，请参阅[查询 AWS CloudTrail 日志](cloudtrail-logs.md)。

```
SELECT
  CAST(useridentity.accountid AS bigint) as newid
FROM cloudtrail_logs
LIMIT 2;
```

此查询返回：

```
+--------------+
| newid        |
+--------------+
| 112233445566 |
+--------------+
| 998877665544 |
+--------------+
```

要查询一个值数组，请发出以下查询：

```
WITH dataset AS (
  SELECT ARRAY[
    CAST(ROW('Bob', 38) AS ROW(name VARCHAR, age INTEGER)),
    CAST(ROW('Alice', 35) AS ROW(name VARCHAR, age INTEGER)),
    CAST(ROW('Jane', 27) AS ROW(name VARCHAR, age INTEGER))
  ] AS users
)
SELECT * FROM dataset
```

它将返回此结果：

```
+-----------------------------------------------------------------+
| users                                                           |
+-----------------------------------------------------------------+
| [{NAME=Bob, AGE=38}, {NAME=Alice, AGE=35}, {NAME=Jane, AGE=27}] |
+-----------------------------------------------------------------+
```

# 筛选具有嵌套值的数组
<a name="filtering-nested-with-dot"></a>

大型数组通常包含嵌套结构，您需要能够对其中的值进行筛选或搜索。

要为包含嵌套 `BOOLEAN` 值的值数组定义数据集，请发出以下查询：

```
WITH dataset AS (
  SELECT
    CAST(
      ROW('aws.amazon.com', ROW(true)) AS ROW(hostname VARCHAR, flaggedActivity ROW(isNew BOOLEAN))
    ) AS sites
)
SELECT * FROM dataset
```

它将返回此结果：

```
+----------------------------------------------------------+
| sites                                                    |
+----------------------------------------------------------+
| {HOSTNAME=aws.amazon.com, FLAGGEDACTIVITY={ISNEW=true}}  |
+----------------------------------------------------------+
```

接下来，要筛选和访问该元素的 `BOOLEAN` 值，请继续使用点 `.` 表示法。

```
WITH dataset AS (
  SELECT
    CAST(
      ROW('aws.amazon.com', ROW(true)) AS ROW(hostname VARCHAR, flaggedActivity ROW(isNew BOOLEAN))
    ) AS sites
)
SELECT sites.hostname, sites.flaggedactivity.isnew
FROM dataset
```

此查询选择嵌套字段并返回此结果：

```
+------------------------+
| hostname       | isnew |
+------------------------+
| aws.amazon.com | true  |
+------------------------+
```

# 使用 `UNNEST` 筛选数组
<a name="filtering-with-unnest"></a>

要按照一个子元素筛选包含嵌套结构的数组，请发出具有 `UNNEST` 运算符的查询。有关 `UNNEST` 的更多信息，请参阅[展平嵌套数组](flattening-arrays.md)。

例如，此查询在数据集中查找站点的主机名。

```
WITH dataset AS (
  SELECT ARRAY[
    CAST(
      ROW('aws.amazon.com', ROW(true)) AS ROW(hostname VARCHAR, flaggedActivity ROW(isNew BOOLEAN))
    ),
    CAST(
      ROW('news.cnn.com', ROW(false)) AS ROW(hostname VARCHAR, flaggedActivity ROW(isNew BOOLEAN))
    ),
    CAST(
      ROW('netflix.com', ROW(false)) AS ROW(hostname VARCHAR, flaggedActivity ROW(isNew BOOLEAN))
    )
  ] as items
)
SELECT sites.hostname, sites.flaggedActivity.isNew
FROM dataset, UNNEST(items) t(sites)
WHERE sites.flaggedActivity.isNew = true
```

它返回：

```
+------------------------+
| hostname       | isnew |
+------------------------+
| aws.amazon.com | true  |
+------------------------+
```

# 使用 `regexp_like` 在数组中查找关键字
<a name="filtering-with-regexp"></a>

以下示例说明如何使用 [regexp\$1like](https://prestodb.io/docs/current/functions/regexp.html) 函数搜索数据集以查找数组内元素中的关键字。它以一个要计算的正则表达式模式（或一个由竖线 (\$1) 分隔的搜索词列表）作为输入，计算此模式，并确定指定的字符串是否包含此模式。

此正则表达式模式需要包含在此字符串内，但并不一定要与此字符串匹配。要匹配整个字符串，请在模式开头使用 ^ 并在末尾使用 \$1 将模式括起来，例如 `'^pattern$'`。

请考虑包含其主机名的站点和 `flaggedActivity` 元素的数组。此元素包含一个 `ARRAY`，其中包含多个 `MAP` 元素，每个元素列出不同的流行关键字及其受欢迎程度计数。假设您要在此数组中的 `MAP` 内查找特定关键字。

要搜索此数据集以查找具有特定关键字的网站，我们使用 `regexp_like` 而不是类似的 SQL `LIKE` 运算符，因为使用 `regexp_like` 搜索大量关键字的效率更高。

**Example 示例 1：使用 `regexp_like`**  
本示例中的查询使用 `regexp_like` 函数来搜索可在数组内的值中找到的词 `'politics|bigdata'`：  

```
WITH dataset AS (
  SELECT ARRAY[
    CAST(
      ROW('aws.amazon.com', ROW(ARRAY[
          MAP(ARRAY['term', 'count'], ARRAY['bigdata', '10']),
          MAP(ARRAY['term', 'count'], ARRAY['serverless', '50']),
          MAP(ARRAY['term', 'count'], ARRAY['analytics', '82']),
          MAP(ARRAY['term', 'count'], ARRAY['iot', '74'])
      ])
      ) AS ROW(hostname VARCHAR, flaggedActivity ROW(flags ARRAY(MAP(VARCHAR, VARCHAR)) ))
   ),
   CAST(
     ROW('news.cnn.com', ROW(ARRAY[
       MAP(ARRAY['term', 'count'], ARRAY['politics', '241']),
       MAP(ARRAY['term', 'count'], ARRAY['technology', '211']),
       MAP(ARRAY['term', 'count'], ARRAY['serverless', '25']),
       MAP(ARRAY['term', 'count'], ARRAY['iot', '170'])
     ])
     ) AS ROW(hostname VARCHAR, flaggedActivity ROW(flags ARRAY(MAP(VARCHAR, VARCHAR)) ))
   ),
   CAST(
     ROW('netflix.com', ROW(ARRAY[
       MAP(ARRAY['term', 'count'], ARRAY['cartoons', '1020']),
       MAP(ARRAY['term', 'count'], ARRAY['house of cards', '112042']),
       MAP(ARRAY['term', 'count'], ARRAY['orange is the new black', '342']),
       MAP(ARRAY['term', 'count'], ARRAY['iot', '4'])
     ])
     ) AS ROW(hostname VARCHAR, flaggedActivity ROW(flags ARRAY(MAP(VARCHAR, VARCHAR)) ))
   )
 ] AS items
),
sites AS (
  SELECT sites.hostname, sites.flaggedactivity
  FROM dataset, UNNEST(items) t(sites)
)
SELECT hostname
FROM sites, UNNEST(sites.flaggedActivity.flags) t(flags)
WHERE regexp_like(flags['term'], 'politics|bigdata')
GROUP BY (hostname)
```
此查询返回两个站点：  

```
+----------------+
| hostname       |
+----------------+
| aws.amazon.com |
+----------------+
| news.cnn.com   |
+----------------+
```

**Example 示例 2：使用 `regexp_like`**  
以下示例中的查询将搜索词与 `regexp_like` 函数匹配的站点的总受欢迎度分数相加，然后将它们从最高到最低排序。  

```
WITH dataset AS (
  SELECT ARRAY[
    CAST(
      ROW('aws.amazon.com', ROW(ARRAY[
          MAP(ARRAY['term', 'count'], ARRAY['bigdata', '10']),
          MAP(ARRAY['term', 'count'], ARRAY['serverless', '50']),
          MAP(ARRAY['term', 'count'], ARRAY['analytics', '82']),
          MAP(ARRAY['term', 'count'], ARRAY['iot', '74'])
      ])
      ) AS ROW(hostname VARCHAR, flaggedActivity ROW(flags ARRAY(MAP(VARCHAR, VARCHAR)) ))
    ),
    CAST(
      ROW('news.cnn.com', ROW(ARRAY[
        MAP(ARRAY['term', 'count'], ARRAY['politics', '241']),
        MAP(ARRAY['term', 'count'], ARRAY['technology', '211']),
        MAP(ARRAY['term', 'count'], ARRAY['serverless', '25']),
        MAP(ARRAY['term', 'count'], ARRAY['iot', '170'])
      ])
      ) AS ROW(hostname VARCHAR, flaggedActivity ROW(flags ARRAY(MAP(VARCHAR, VARCHAR)) ))
    ),
    CAST(
      ROW('netflix.com', ROW(ARRAY[
        MAP(ARRAY['term', 'count'], ARRAY['cartoons', '1020']),
        MAP(ARRAY['term', 'count'], ARRAY['house of cards', '112042']),
        MAP(ARRAY['term', 'count'], ARRAY['orange is the new black', '342']),
        MAP(ARRAY['term', 'count'], ARRAY['iot', '4'])
      ])
      ) AS ROW(hostname VARCHAR, flaggedActivity ROW(flags ARRAY(MAP(VARCHAR, VARCHAR)) ))
    )
  ] AS items
),
sites AS (
  SELECT sites.hostname, sites.flaggedactivity
  FROM dataset, UNNEST(items) t(sites)
)
SELECT hostname, array_agg(flags['term']) AS terms, SUM(CAST(flags['count'] AS INTEGER)) AS total
FROM sites, UNNEST(sites.flaggedActivity.flags) t(flags)
WHERE regexp_like(flags['term'], 'politics|bigdata')
GROUP BY (hostname)
ORDER BY total DESC
```
此查询返回两个站点：  

```
+------------------------------------+
| hostname       | terms    | total  |
+----------------+-------------------+
| news.cnn.com   | politics |  241   |
+----------------+-------------------+
| aws.amazon.com | bigdata |  10    |
+----------------+-------------------+
```

# 查询地理空间数据
<a name="querying-geospatial-data"></a>

地理空间数据包含为对象指定地理位置的标识符。此类数据的示例包括天气报告、地图方向、包含地理位置的推特、店铺位置以及航空公司路线。地理空间数据在业务分析、报告和预测中起着重要的作用。

地理空间标识符 (如经度和纬度) 允许您将任何邮寄地址转换为一组地理坐标。

## 什么是地理空间查询？
<a name="geospatial-query-what-is"></a>

地理空间查询是在 Athena 中支持的专门类型 SQL 查询。它们在以下方面与非空间 SQL 查询不同：
+ 使用以下专门 geometry 数据类型：`point`、`line`、`multiline`、`polygon` 和 `multipolygon`。
+ 表示 geometry 数据类型之间的关系，例如 `distance`、`equals`、`crosses`、`touches`、`overlaps`、`disjoint` 等。

通过在 Athena 中使用地理空间查询，您可以运行这些操作和其他类似操作：
+ 找出两点之间的距离。
+ 检查一个区域 (多边形) 是否包含另一个区域。
+ 检查一条线是否穿过或接触另一条线或多边形。

例如，要在 Athena 中从雷尼尔山地理坐标的类型 `double` 的值获得 `point` 几何数据类型，请使用 `ST_Point (longitude, latitude)` 几何空间函数，如以下示例所示。

```
ST_Point(-121.7602, 46.8527)
```

## 输入数据格式和 Geometry 数据类型
<a name="geospatial-input-data-formats-supported-geometry-types"></a>

要在 Athena 中使用地理空间函数，请以 WKT 格式输入数据，或使用 Hive JSON SerDe。您还可以使用在 Athena 中受支持的几何数据类型。

### 输入数据格式
<a name="input-data-formats"></a>

为处理地理空间查询，Athena 支持以下数据格式的输入数据：
+  **WKT (已知文本)**。在 Athena 中，WKT 表示为 `varchar(x)` 或 `string` 数据类型。
+  **JSON 编码的地理空间数据**。为解析具有地理空间数据的 JSON 文件并为它们创建表，Athena 使用 [Hive JSON SerDe](https://github.com/Esri/spatial-framework-for-hadoop/wiki/Hive-JSON-SerDe)。有关在 Athena 中使用此 SerDe 的更多信息，请参阅 [JSON SerDe 库](json-serde.md)。

### Geometry 数据类型
<a name="geometry-data-types"></a>

为处理地理空间查询，Athena 支持以下专用几何数据类型：
+  `point` 
+  `line` 
+  `polygon` 
+  `multiline` 
+  `multipolygon` 

## 支持的地理空间函数
<a name="geospatial-functions-list"></a>

有关 Athena 引擎版本 3 中地理空间函数的信息，请参阅 Trino 文档中的 [地理空间函数](https://trino.io/docs/current/functions/geospatial.html)。

# 示例：地理空间查询
<a name="geospatial-example-queries"></a>

本主题中的示例从 GitHub 上提供的示例数据创建两个表，并根据数据查询这两个表。以下文件中的示例数据仅用于说明目的，并不能保证准确性：
+ **[https://github.com/Esri/gis-tools-for-hadoop/blob/master/samples/data/earthquake-data/earthquakes.csv](https://github.com/Esri/gis-tools-for-hadoop/blob/master/samples/data/earthquake-data/earthquakes.csv)** – 列出了加利福尼亚发生的地震。示例 `earthquakes` 表使用此数据中的字段。
+ **[https://github.com/Esri/gis-tools-for-hadoop/blob/master/samples/data/counties-data/california-counties.json](https://github.com/Esri/gis-tools-for-hadoop/blob/master/samples/data/counties-data/california-counties.json)** – 使用[符合 ESRI 标准的 GeoJSON 格式](https://doc.arcgis.com/en/arcgis-online/reference/geojson.htm)列出加利福尼亚州的县级数据。此数据包含多个字段，例如 `AREA`、`PERIMETER`、`STATE`、`COUNTY` 和 `NAME`，但示例 `counties` 表仅使用两个字段：`Name`（字符串）和 `BoundaryShape`（二进制）。
**注意**  
Athena 使用 `com.esri.json.hadoop.EnclosedEsriJsonInputFormat` 将 JSON 数据转换为地理空间二进制格式。

以下代码示例创建一个名为 `earthquakes` 的表：

```
CREATE external TABLE earthquakes
(
 earthquake_date string,
 latitude double,
 longitude double,
 depth double,
 magnitude double,
 magtype string,
 mbstations string,
 gap string,
 distance string,
 rms string,
 source string,
 eventid string
)
ROW FORMAT DELIMITED FIELDS TERMINATED BY ','
STORED AS TEXTFILE LOCATION 's3://amzn-s3-demo-bucket/my-query-log/csv/';
```

以下代码示例创建一个名为 `counties` 的表：

```
CREATE external TABLE IF NOT EXISTS counties
 (
 Name string,
 BoundaryShape binary
 )
ROW FORMAT SERDE 'com.esri.hadoop.hive.serde.EsriJsonSerDe'
STORED AS INPUTFORMAT 'com.esri.json.hadoop.EnclosedEsriJsonInputFormat'
OUTPUTFORMAT 'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'
LOCATION 's3://amzn-s3-demo-bucket/my-query-log/json/';
```

以下示例查询在 `counties` 和 `earthquake` 表中使用 `CROSS JOIN` 函数。此示例使用 `ST_CONTAINS` 来查询边界包含地震位置的县，这些位置由 `ST_POINT` 指定。然后，查询会按名称对这些县进行分组，然后按计数对其进行排序，并以降序返回它们。

```
SELECT counties.name,
        COUNT(*) cnt
FROM counties
CROSS JOIN earthquakes
WHERE ST_CONTAINS (ST_GeomFromLegacyBinary(counties.boundaryshape), ST_POINT(earthquakes.longitude, earthquakes.latitude))
GROUP BY  counties.name
ORDER BY  cnt DESC
```

此查询返回：

```
+------------------------+
| name             | cnt |
+------------------------+
| Kern             | 36  |
+------------------------+
| San Bernardino   | 35  |
+------------------------+
| Imperial         | 28  |
+------------------------+
| Inyo             | 20  |
+------------------------+
| Los Angeles      | 18  |
+------------------------+
| Riverside        | 14  |
+------------------------+
| Monterey         | 14  |
+------------------------+
| Santa Clara      | 12  |
+------------------------+
| San Benito       | 11  |
+------------------------+
| Fresno           | 11  |
+------------------------+
| San Diego        | 7   |
+------------------------+
| Santa Cruz       | 5   |
+------------------------+
| Ventura          | 3   |
+------------------------+
| San Luis Obispo  | 3   |
+------------------------+
| Orange           | 2   |
+------------------------+
| San Mateo        | 1   |
+------------------------+
```

## 其他资源
<a name="geospatial-example-queries-additional-resources"></a>

有关地理空间查询的更多示例，请参阅以下博客文章：
+ [在 Amazon Athena 中使用 UDF 和 AWS Lambda 扩展地理空间查询](https://aws.amazon.com/blogs/big-data/extend-geospatial-queries-in-amazon-athena-with-udfs-and-aws-lambda/) 
+ [使用 Amazon Athena 和 Amazon Quick 可视化超过 200 年的全球气候数据。](https://aws.amazon.com/blogs/big-data/visualize-over-200-years-of-global-climate-data-using-amazon-athena-and-amazon-quicksight/)
+ [使用 Amazon Athena 查询 OpenStreetMap](https://aws.amazon.com/blogs/big-data/querying-openstreetmap-with-amazon-athena/)

# 查询 JSON 数据
<a name="querying-JSON"></a>

Amazon Athena 可让您查询 JSON 编码的数据，从嵌套 JSON 中提取数据，搜索值，以及查找 JSON 数组的长度和大小。要了解在 Athena 中查询 JSON 数据的基础知识，请考虑下列星球数据示例：

```
{name:"Mercury",distanceFromSun:0.39,orbitalPeriod:0.24,dayLength:58.65}
{name:"Venus",distanceFromSun:0.72,orbitalPeriod:0.62,dayLength:243.02}
{name:"Earth",distanceFromSun:1.00,orbitalPeriod:1.00,dayLength:1.00}
{name:"Mars",distanceFromSun:1.52,orbitalPeriod:1.88,dayLength:1.03}
```

请注意每个记录（实际上是表中的每一行）是如何位于一个单独的行上的。要查询此 JSON 数据，可以使用如下 `CREATE TABLE` 语句：

```
CREATE EXTERNAL TABLE `planets_json`(
  `name` string,
  `distancefromsun` double,
  `orbitalperiod` double,
  `daylength` double)
ROW FORMAT SERDE
  'org.openx.data.jsonserde.JsonSerDe'
STORED AS INPUTFORMAT
  'org.apache.hadoop.mapred.TextInputFormat'
OUTPUTFORMAT
  'org.apache.hadoop.hive.ql.io.IgnoreKeyTextOutputFormat'
LOCATION
  's3://amzn-s3-demo-bucket/json/'
```

要查询数据，请使用类似以下示例的简单 `SELECT` 语句。

```
SELECT * FROM planets_json
```

查询结果如下所示。


****  

| \$1 | name | distancefromsun | orbitalperiod | daylength | 
| --- | --- | --- | --- | --- | 
| 1 | 水星 | 0.39 | 0.24 | 58.65 | 
| 2 | 金星 | 0.72 | 0.62 | 243.02 | 
| 3 | 地球 | 1.0 | 1.0 | 1.0 | 
| 4 | 火星 | 1.52 | 1.88 | 1.03 | 

请注意 `CREATE TABLE` 语句是如何使用 [OpenX JSON SerDe](openx-json-serde.md) 的，它要求每个 JSON 记录都在一个单独的行上。如果 JSON 采用漂亮打印格式，或者所有记录都在一行上，则数据将无法正确读取。

要查询采用漂亮打印格式的 JSON 数据，可以使用 [Amazon Ion Hive SerDe](ion-serde.md) 代替 OpenX JSON Serde。考虑以前以漂亮打印格式存储的数据：

```
{
  name:"Mercury",
  distanceFromSun:0.39,
  orbitalPeriod:0.24,
  dayLength:58.65
}
{
  name:"Venus",
  distanceFromSun:0.72,
  orbitalPeriod:0.62,
  dayLength:243.02
}
{
  name:"Earth",
  distanceFromSun:1.00,
  orbitalPeriod:1.00,
  dayLength:1.00
}
{
  name:"Mars",
  distanceFromSun:1.52,
  orbitalPeriod:1.88,
  dayLength:1.03
}
```

要在不重新设置格式的情况下查询这些数据，可以使用如下 `CREATE TABLE` 语句。请注意，语句不是指定 OpenX JSON SerDe，而是指定 `STORED AS ION`。

```
CREATE EXTERNAL TABLE `planets_ion`(
  `name` string,
  `distancefromsun` DECIMAL(10, 2),
  `orbitalperiod` DECIMAL(10, 2),
  `daylength` DECIMAL(10, 2))
STORED AS ION
LOCATION
  's3://amzn-s3-demo-bucket/json-ion/'
```

查询 `SELECT * FROM planets_ion` 产生的结果与以前相同。有关使用 Amazon Ion Hive SerDe 以这种方式创建表的更多信息，请参阅 [创建 Amazon Ion 表](ion-serde-using-create-table.md)。

前面的示例 JSON 数据不包含复杂的数据类型，如嵌套数组或结构。有关查询嵌套 JSON 数据的更多信息，请参阅 [示例：反序列化嵌套 JSON](openx-json-serde.md#nested-json-serde-example)。

**Topics**
+ [读取 JSON 数据的最佳实践](parsing-json-data.md)
+ [从字符串中提取 JSON 数据](extracting-data-from-JSON.md)
+ [在 JSON 数组中搜索值](searching-for-values.md)
+ [获取 JSON 数组的长度和大小](length-and-size.md)
+ [JSON 查询问题排查](json-troubleshooting.md)

# 读取 JSON 数据的最佳实践
<a name="parsing-json-data"></a>

JavaScript 对象表示法 (JSON) 是将数据结构编码为文本的常用方法。许多应用程序和工具输出 JSON 编码的数据。

在 Amazon Athena 中，您可以从外部数据创建表，并在其中包含 JSON 编码的数据。对于此类类型的源数据，请将 Athena 与 [JSON SerDe 库](json-serde.md) 结合使用。

使用以下提示来读取 JSON 编码的数据：
+ 选择正确的 SerDe、本机 JSON SerDe、`org.apache.hive.hcatalog.data.JsonSerDe` 或 OpenX SerDe、`org.openx.data.jsonserde.JsonSerDe`。有关更多信息，请参阅 [JSON SerDe 库](json-serde.md)。
+ 确保每个 JSON 编码的记录表示在单独的行上，而不是采用美观的打印格式。
**注意**  
SerDe 期望每个 JSON 文档都位于单行文本中，并且不使用行终止字符分隔记录中的字段。如果 JSON 文本采用美观的打印格式，当您在创建表后尝试对其进行查询时，可能会收到类似以下内容的错误消息：HIVE\$1CURSOR\$1ERROR: Row is not a valid JSON Object（HIVE\$1CURSOR\$1ERROR：行不是有效的 JSON 对象）或 HIVE\$1CURSOR\$1ERROR: JsonParseException: Unexpected end-of-input: expected close marker for OBJECT（HIVE\$1CURSOR\$1ERROR：JsonParseException：意外的输入结束：对象的预期关闭标记）。有关更多信息，请参阅 GitHub 上 OpenX SerDe 文档中的 [JSON 数据文件](https://github.com/rcongiu/Hive-JSON-Serde#json-data-files)。
+ 在不区分大小写的列中生成 JSON 编码的数据。
+ 提供用于忽略格式错误的记录的选项，如本示例中所示。

  ```
  CREATE EXTERNAL TABLE json_table (
    column_a string,
    column_b int
   )
   ROW FORMAT SERDE 'org.openx.data.jsonserde.JsonSerDe'
   WITH SERDEPROPERTIES ('ignore.malformed.json' = 'true')
   LOCATION 's3://amzn-s3-demo-bucket/path/';
  ```
+ 将源数据中具有待定架构的字段转换为 Athena 中的 JSON 编码字符串。

当 Athena 创建由 JSON 数据提供支持的表时，它会基于现有的和预定义的架构解析数据。但并非您的所有数据都可能具有预定义的架构。要在这种情况下简化架构管理，通常有用的做法是将源数据中具有不确定架构的字段转换为 Athena 中的 JSON 字符串，然后使用 [JSON SerDe 库](json-serde.md)。

例如，请考虑一个从不同传感器发布具有公共字段的事件的 IoT 应用程序。其中一个字段必须存储一个对发送事件的传感器唯一的自定义有效负载。在这种情况下，因为您不知道架构，我们建议您将信息存储为 JSON 编码的字符串。为此，请将 Athena 表中的数据转换为 JSON，如下例所示。您还可以将 JSON 编码的数据转换为 Athena 数据类型。

**Topics**
+ [将 Athena 数据转换为 JSON](converting-native-data-types-to-json.md)
+ [将 JSON 转换为 Athena 数据类型](converting-json-to-native-data-types.md)

# 将 Athena 数据转换为 JSON
<a name="converting-native-data-types-to-json"></a>

要将 Athena 数据转换为 JSON 数据类型，请使用 `CAST`。

```
WITH dataset AS (
  SELECT
    CAST('HELLO ATHENA' AS JSON) AS hello_msg,
    CAST(12345 AS JSON) AS some_int,
    CAST(MAP(ARRAY['a', 'b'], ARRAY[1,2]) AS JSON) AS some_map
)
SELECT * FROM dataset
```

此查询返回：

```
+-------------------------------------------+
| hello_msg      | some_int | some_map      |
+-------------------------------------------+
| "HELLO ATHENA" | 12345    | {"a":1,"b":2} |
+-------------------------------------------+
```

# 将 JSON 转换为 Athena 数据类型
<a name="converting-json-to-native-data-types"></a>

要将 JSON 数据转换为 Athena 数据类型，请使用 `CAST`。

**注意**  
在本示例中，为将字符串表示为 JSON 编码，一开始就使用 `JSON` 关键字并使用单引号，例如 `JSON '12345'` 

```
WITH dataset AS (
  SELECT
    CAST(JSON '"HELLO ATHENA"' AS VARCHAR) AS hello_msg,
    CAST(JSON '12345' AS INTEGER) AS some_int,
    CAST(JSON '{"a":1,"b":2}' AS MAP(VARCHAR, INTEGER)) AS some_map
)
SELECT * FROM dataset
```

此查询返回：

```
+-------------------------------------+
| hello_msg    | some_int | some_map  |
+-------------------------------------+
| HELLO ATHENA | 12345    | {a:1,b:2} |
+-------------------------------------+
```

# 从字符串中提取 JSON 数据
<a name="extracting-data-from-JSON"></a>

您可能拥有包含 JSON 编码字符串的源数据，您不一定要将其反序列化到 Athena 的表中。在这种情况下，您仍然可以使用 Presto 中提供的 JSON 函数对此数据运行 SQL 操作。

请考虑以下 JSON 字符串作为示例数据集。

```
{"name": "Susan Smith",
"org": "engineering",
"projects":
    [
     {"name":"project1", "completed":false},
     {"name":"project2", "completed":true}
    ]
}
```

## 示例：提取属性
<a name="examples-extracting-properties"></a>

要从 JSON 字符串中提取 `name` 和 `projects` 属性，请使用 `json_extract` 函数，如以下示例所示。`json_extract` 函数将采用包含 JSON 字符串的列，并使用类似 `JSONPath` 的表达式 (使用点 `.` 表示法) 搜索它。

**注意**  
 `JSONPath` 执行简单的树遍历。它使用 `$` 符号来表示 JSON 文档的根，后跟一个句点和一个直接位于根下面的嵌套元素，例如 `$.name`。

```
WITH dataset AS (
  SELECT '{"name": "Susan Smith",
           "org": "engineering",
           "projects": [{"name":"project1", "completed":false},
           {"name":"project2", "completed":true}]}'
    AS myblob
)
SELECT
  json_extract(myblob, '$.name') AS name,
  json_extract(myblob, '$.projects') AS projects
FROM dataset
```

返回的值是一个 JSON 编码的字符串，而不是本机 Athena 数据类型。

```
+-----------------------------------------------------------------------------------------------+
| name           | projects                                                                     |
+-----------------------------------------------------------------------------------------------+
| "Susan Smith"  | [{"name":"project1","completed":false},{"name":"project2","completed":true}] |
+-----------------------------------------------------------------------------------------------+
```

要从 JSON 字符串中提取标量值，请使用 `json_extract_scalar(json, json_path)` 函数。它类似于 `json_extract`，但返回的是 `varchar` 字符串值而不是 JSON 编码的字符串。*json\$1path* 参数的值必须是标量（布尔值、数字或字符串）。

**注意**  
请勿对数组、映射或结构使用 `json_extract_scalar` 函数。

```
WITH dataset AS (
  SELECT '{"name": "Susan Smith",
           "org": "engineering",
           "projects": [{"name":"project1", "completed":false},{"name":"project2", "completed":true}]}'
    AS myblob
)
SELECT
  json_extract_scalar(myblob, '$.name') AS name,
  json_extract_scalar(myblob, '$.projects') AS projects
FROM dataset
```

此查询返回：

```
+---------------------------+
| name           | projects |
+---------------------------+
| Susan Smith    |          |
+---------------------------+
```

要获取示例数组中的 `projects` 属性的第一个元素，请使用 `json_array_get` 函数并指定索引位置。

```
WITH dataset AS (
  SELECT '{"name": "Bob Smith",
           "org": "engineering",
           "projects": [{"name":"project1", "completed":false},{"name":"project2", "completed":true}]}'
    AS myblob
)
SELECT json_array_get(json_extract(myblob, '$.projects'), 0) AS item
FROM dataset
```

它返回 JSON 编码数组中指定索引位置处的值。

```
+---------------------------------------+
| item                                  |
+---------------------------------------+
| {"name":"project1","completed":false} |
+---------------------------------------+
```

要返回一个 Athena 字符串类型，请在 `JSONPath` 表达式中使用 `[]` 运算符，然后使用 `json_extract_scalar` 函数。有关 `[]` 的更多信息，请参阅 [访问数组元素](accessing-array-elements.md)。

```
WITH dataset AS (
   SELECT '{"name": "Bob Smith",
             "org": "engineering",
             "projects": [{"name":"project1", "completed":false},{"name":"project2", "completed":true}]}'
     AS myblob
)
SELECT json_extract_scalar(myblob, '$.projects[0].name') AS project_name
FROM dataset
```

它将返回此结果：

```
+--------------+
| project_name |
+--------------+
| project1     |
+--------------+
```

# 在 JSON 数组中搜索值
<a name="searching-for-values"></a>

要确定特定值是否存在于 JSON 编码的数组内，请使用 `json_array_contains` 函数。

以下查询列出参加“project2”的用户的名称。

```
WITH dataset AS (
  SELECT * FROM (VALUES
    (JSON '{"name": "Bob Smith", "org": "legal", "projects": ["project1"]}'),
    (JSON '{"name": "Susan Smith", "org": "engineering", "projects": ["project1", "project2", "project3"]}'),
    (JSON '{"name": "Jane Smith", "org": "finance", "projects": ["project1", "project2"]}')
  ) AS t (users)
)
SELECT json_extract_scalar(users, '$.name') AS user
FROM dataset
WHERE json_array_contains(json_extract(users, '$.projects'), 'project2')
```

此查询会返回一个用户列表。

```
+-------------+
| user        |
+-------------+
| Susan Smith |
+-------------+
| Jane Smith  |
+-------------+
```

以下查询示例列出了完成项目的用户的名称以及已完成的项目的总数。它执行以下操作：
+ 为清晰起见，使用嵌套 `SELECT` 语句。
+ 提取项目的数组。
+ 使用 `CAST` 将数组转换为键-值对的本机数组。
+ 使用 `UNNEST` 运算符提取每个数组元素。
+ 按完成项目来筛选获取的值，并对其进行计数。

**注意**  
当使用 `CAST` 到 `MAP` 时，您可以将键元素指定为 `VARCHAR`（Presto 中的本机字符串），但将值保留为 JSON，因为 `MAP` 中的值具有不同类型：第一个键-值对为字符串，第二个键-值对为布尔型。

```
WITH dataset AS (
  SELECT * FROM (VALUES
    (JSON '{"name": "Bob Smith",
             "org": "legal",
             "projects": [{"name":"project1", "completed":false}]}'),
    (JSON '{"name": "Susan Smith",
             "org": "engineering",
             "projects": [{"name":"project2", "completed":true},
                          {"name":"project3", "completed":true}]}'),
    (JSON '{"name": "Jane Smith",
             "org": "finance",
             "projects": [{"name":"project2", "completed":true}]}')
  ) AS t (users)
),
employees AS (
  SELECT users, CAST(json_extract(users, '$.projects') AS
    ARRAY(MAP(VARCHAR, JSON))) AS projects_array
  FROM dataset
),
names AS (
  SELECT json_extract_scalar(users, '$.name') AS name, projects
  FROM employees, UNNEST (projects_array) AS t(projects)
)
SELECT name, count(projects) AS completed_projects FROM names
WHERE cast(element_at(projects, 'completed') AS BOOLEAN) = true
GROUP BY name
```

此查询返回以下结果：

```
+----------------------------------+
| name        | completed_projects |
+----------------------------------+
| Susan Smith | 2                  |
+----------------------------------+
| Jane Smith  | 1                  |
+----------------------------------+
```

# 获取 JSON 数组的长度和大小
<a name="length-and-size"></a>

要获取 JSON 数组的长度和大小，可以使用 `json_array_length` 和 `json_size` 函数。

## 例如：`json_array_length`
<a name="example-json-array-length"></a>

要获取 JSON 编码数组的长度，请使用 `json_array_length` 函数。

```
WITH dataset AS (
  SELECT * FROM (VALUES
    (JSON '{"name":
            "Bob Smith",
            "org":
            "legal",
            "projects": [{"name":"project1", "completed":false}]}'),
    (JSON '{"name": "Susan Smith",
            "org": "engineering",
            "projects": [{"name":"project2", "completed":true},
                         {"name":"project3", "completed":true}]}'),
    (JSON '{"name": "Jane Smith",
             "org": "finance",
             "projects": [{"name":"project2", "completed":true}]}')
  ) AS t (users)
)
SELECT
  json_extract_scalar(users, '$.name') as name,
  json_array_length(json_extract(users, '$.projects')) as count
FROM dataset
ORDER BY count DESC
```

此查询返回以下结果：

```
+---------------------+
| name        | count |
+---------------------+
| Susan Smith | 2     |
+---------------------+
| Bob Smith   | 1     |
+---------------------+
| Jane Smith  | 1     |
+---------------------+
```

## 例如：`json_size`
<a name="example-json-size"></a>

要获取 JSON 编码的数组或对象的大小，请使用 `json_size` 函数，并指定将 JSON 字符串和 `JSONPath` 表达式包含到该数组或对象中的列。

```
WITH dataset AS (
  SELECT * FROM (VALUES
    (JSON '{"name": "Bob Smith", "org": "legal", "projects": [{"name":"project1", "completed":false}]}'),
    (JSON '{"name": "Susan Smith", "org": "engineering", "projects": [{"name":"project2", "completed":true},{"name":"project3", "completed":true}]}'),
    (JSON '{"name": "Jane Smith", "org": "finance", "projects": [{"name":"project2", "completed":true}]}')
  ) AS t (users)
)
SELECT
  json_extract_scalar(users, '$.name') as name,
  json_size(users, '$.projects') as count
FROM dataset
ORDER BY count DESC
```

此查询返回以下结果：

```
+---------------------+
| name        | count |
+---------------------+
| Susan Smith | 2     |
+---------------------+
| Bob Smith   | 1     |
+---------------------+
| Jane Smith  | 1     |
+---------------------+
```

# JSON 查询问题排查
<a name="json-troubleshooting"></a>

有关排查 JSON 相关查询的问题的帮助，请参阅 [JSON 相关错误](troubleshooting-athena.md#troubleshooting-athena-json-related-errors) 或咨询以下资源：
+ [我在 Amazon Athena 中尝试读取 JSON 数据时收到错误](https://aws.amazon.com/premiumsupport/knowledge-center/error-json-athena/)
+ [从 Athena 中的 AWS Config 读取文件时，如何解决“HIVE\$1CURSOR\$1ERROR: Row is not a valid JSON Object – JSONException: Duplicate key（HIVE\$1CURSOR\$1ERROR：行是无效的 JSON 对象 – JSONException：重复键）”错误？](https://aws.amazon.com/premiumsupport/knowledge-center/json-duplicate-key-error-athena-config/)
+ [即使输入 JSON 文件有多条记录，Amazon Athena 中的 SELECT COUNT 查询也仅返回一条记录](https://aws.amazon.com/premiumsupport/knowledge-center/select-count-query-athena-json-records/)
+ [如何在 Amazon S3 源文件中查找 Athena 表中的某行？](https://aws.amazon.com/premiumsupport/knowledge-center/find-s3-source-file-athena-table-row/)

另请参阅 [Amazon Athena 中 SQL 查询的注意事项和限制](other-notable-limitations.md)。

# 将机器学习（ML）与 Amazon Athena 结合使用
<a name="querying-mlmodel"></a>

通过将机器学习（ML）与 Amazon Athena 结合使用，您可以借助 Athena 编写将使用 Amazon SageMaker AI 运行机器学习（ML）推理的 SQL 语句。此功能简化了对 ML 模型的访问以进行数据分析，无需使用复杂的编程方法来运行推理。

要将机器学习（ML）与 Athena 结合使用，您可以使用 `USING EXTERNAL FUNCTION` 子句定义将机器学习（ML）与 Athena 结合的函数。该函数指向要使用的 SageMaker AI 模型端点，并指定要传递给模型的变量名称和数据类型。查询中的后续子句引用该函数以将值传递给模型。模型根据查询传递的值运行推理，然后返回推理结果。有关 SageMaker AI 以及 SageMaker AI 端点工作原理的信息，请参阅 [Amazon SageMaker AI 开发人员指南](https://docs.aws.amazon.com/sagemaker/latest/dg/)。

有关将 ML 与 Athena 和 SageMaker AI 推理结果结合使用来检测结果集中异常值的示例，请参阅 AWS 大数据博客文章：[Detecting anomalous values by invoking the Amazon Athena machine learning inference function](https://aws.amazon.com/blogs/big-data/detecting-anomalous-values-by-invoking-the-amazon-athena-machine-learning-inference-function/)。

## 注意事项和限制
<a name="considerations-and-limitations"></a>
+ **可用区域** – Athena ML 功能已在支持 Athena 引擎版本 2 的 AWS 区域开放。
+ **SageMaker AI 模型端点必须接受并返回 `text/csv`** – 有关数据格式的更多信息，请参阅《Amazon SageMaker 开发人员指南》中的 [Common data formats for inference](https://docs.aws.amazon.com/sagemaker/latest/dg/cdf-inference.html)**。
+ **Athena 不发送 CSV 标头** – 如果您的 SageMaker AI 端点是 `text/csv`，则您的输入处理程序不应假设输入的第一行是 CSV 标头。由于 Athena 不发送 CSV 标头，因此返回给 Athena 的输出将比 Athena 预期的少一行，从而导致错误。
+ **SageMaker AI 端点扩展** – 确保引用的 SageMaker AI 模型端点已充分纵向扩展，以便 Athena 调用该端点。有关更多信息，请参阅《Amazon SageMaker AI 开发人员指南》中的 [Automatically scale SageMaker AI models](https://docs.aws.amazon.com/sagemaker/latest/dg/endpoint-auto-scaling.html)** 和《Amazon SageMaker AI API 参考》中的 [CreateEndpointConfig](https://docs.aws.amazon.com/sagemaker/latest/dg/API_CreateEndpointConfig.html)**。
+ **IAM 权限** – 要运行指定将 ML 与 Athena 相结合的函数的查询，必须允许运行该查询的 IAM 主体为引用的 SageMaker AI 模型端点执行 `sagemaker:InvokeEndpoint` 操作。有关更多信息，请参阅 [允许使用 Athena 访问机器学习](machine-learning-iam-access.md)。
+ **机器学习与 Athena 相结合的函数不能直接在 `GROUP BY` 子句中使用**

**Topics**
+ [注意事项和限制](#considerations-and-limitations)
+ [将机器学习（ML）与 Athena 结合使用的语法](ml-syntax.md)
+ [请参阅客户使用示例](ml-videos.md)

# 将机器学习（ML）与 Athena 结合使用的语法
<a name="ml-syntax"></a>

`USING EXTERNAL FUNCTION` 子句指定可由查询中的后续 `SELECT` 语句引用的机器学习（ML）与 Athena 相结合的函数或多个函数。您定义函数名称、变量名称以及变量和返回值的数据类型。

## 摘要
<a name="ml-synopsis"></a>

下面的语法显示了 `USING EXTERNAL FUNCTION` 子句，该子句指定了机器学习（ML）与 Athena 相结合的函数。

```
USING EXTERNAL FUNCTION ml_function_name (variable1 data_type[, variable2 data_type][,...])
RETURNS data_type 
SAGEMAKER 'sagemaker_endpoint'
SELECT ml_function_name()
```

## 参数
<a name="udf-parameters"></a>

**USING EXTERNAL FUNCTION *ml\$1function\$1name* (*variable1**data\$1type*[, *variable2**data\$1type*][,...])**  
*ml\$1function\$1name* 定义函数名称，可以在后续查询子句中使用该函数名称。每个 *variable data\$1type* 都指定一个命名变量，且其相应的数据类型应可为 SageMaker AI 模型接受为输入。指定的数据类型必须是支持的 Athena 数据类型。

**RETURNS *data\$1type***  
*data\$1type* 指定作为 SageMaker AI 模型的输出由 *ml\$1function\$1name* 返回到查询的 SQL 数据类型。

**SAGEMAKER '*sagemaker\$1endpoint*'**  
*sagemaker\$1endpoint* 指定 SageMaker AI 模型的端点。

**SELECT [...]*ml\$1function\$1name*(*expression*) [...]**  
将值传递给函数变量和 SageMaker AI 模型以返回结果的 SELECT 查询。*ml\$1function\$1name* 指定之前在查询中定义的函数，后跟进行计算以传递值的*表达式*。传递和返回的值必须与 `USING EXTERNAL FUNCTION` 子句中为函数指定的相应数据类型匹配。

## 示例
<a name="ml-examples"></a>

以下示例演示了使用机器学习（ML）与 Athena 结合的查询。

**Example**  

```
USING EXTERNAL FUNCTION predict_customer_registration(age INTEGER) 
    RETURNS DOUBLE
    SAGEMAKER 'xgboost-2019-09-20-04-49-29-303' 
SELECT predict_customer_registration(age) AS probability_of_enrolling, customer_id 
     FROM "sampledb"."ml_test_dataset" 
     WHERE predict_customer_registration(age) < 0.5;
```

# 请参阅客户使用示例
<a name="ml-videos"></a>

以下视频使用了机器学习（ML）与 Amazon Athena 相结合函数的预览版，展示了您可以将 Athena 与 SageMaker AI 结合使用的方式。

## 预测客户流失
<a name="ml-videos-predict-churn"></a>

以下视频展示了如何将 Athena 与 Amazon SageMaker AI 的机器学习功能相结合，从而预测客户流失情况。

[![AWS Videos](http://img.youtube.com/vi/https://www.youtube.com/embed/CUHbSpekRVg/0.jpg)](http://www.youtube.com/watch?v=https://www.youtube.com/embed/CUHbSpekRVg)


## 检测僵尸网络
<a name="ml-videos-detect-botnets"></a>

以下视频展示了一家公司如何使用 Amazon Athena 和 Amazon SageMaker AI 来检测僵尸网络。

[![AWS Videos](http://img.youtube.com/vi/https://www.youtube.com/embed/0dUv-jCt2aw/0.jpg)](http://www.youtube.com/watch?v=https://www.youtube.com/embed/0dUv-jCt2aw)


# 使用用户定义函数进行查询
<a name="querying-udf"></a>

Amazon Athena 中的用户定义函数 (UDF) 使您可以创建自定义函数来处理记录或记录组。UDF 接受参数，执行工作，然后返回结果。

要在 Athena 中使用 UDF，请在 SQL 查询中的 `SELECT` 语句之前写入 `USING EXTERNAL FUNCTION` 子句。`SELECT` 语句引用 UDF 并定义在查询运行时传递给 UDF 的变量。SQL 查询在调用 UDF 时使用 Java 运行时调用 Lambda 函数。UDF 在 Lambda 函数中定义为 Java 部署包中的方法。可以在同一个 Java 部署包中为某个 Lambda 函数定义多个 UDF。您还可以在 `USING EXTERNAL FUNCTION` 子句中指定 Lambda 函数的名称。

部署 Athena UDF 的 Lambda 函数有两个选项。您可以直接使用 Lambda 部署函数，也可以使用 AWS Serverless Application Repository 来部署。要查找 UDF 的现有 Lambda 函数，您可以搜索公共 AWS Serverless Application Repository 或私有存储库，然后部署到 Lambda。您还可以创建或修改 Java 源代码，将其打包到 JAR 文件中，然后使用 Lambda 或 AWS Serverless Application Repository 来部署它。有关能够帮助您开始使用的示例 Java 源代码和软件包，请参阅 [使用 Lambda 创建和部署 UDF](udf-creating-and-deploying.md)。有关 Lambda 的更多信息，请参阅《[AWS Lambda 开发人员指南](https://docs.aws.amazon.com/lambda/latest/dg/)》。有关 AWS Serverless Application Repository 的更多信息，请参阅《[AWS Serverless Application Repository 开发人员指南](https://docs.aws.amazon.com/serverlessrepo/latest/devguide/)》。

有关使用 UDF 与 Athena 一起翻译和分析文本的示例，请参阅 AWS Machine Learning 博客文章：[使用 Amazon Athena、Amazon Translate 和 Amazon Comprehend 的 SQL 函数翻译和分析文本](https://aws.amazon.com/blogs/machine-learning/translate-and-analyze-text-using-sql-functions-with-amazon-athena-amazon-translate-and-amazon-comprehend/)，或观看 [video](udf-videos.md#udf-videos-xlate)。

有关在 Amazon Athena 中使用 UDF 扩展地理空间查询的示例，请参阅 *AWS 大数据博客*中的[使用 UDF 和 AWS Lambda 在 Amazon Athena 中扩展地理空间查询](https://aws.amazon.com/blogs/big-data/extend-geospatial-queries-in-amazon-athena-with-udfs-and-aws-lambda/)。

**Topics**
+ [Athena 中关于 UDF 的视频](udf-videos.md)
+ [注意事项和限制](udf-considerations-limitations.md)
+ [使用 UDF 查询语法进行查询](udf-query-syntax.md)
+ [使用 Lambda 创建和部署 UDF](udf-creating-and-deploying.md)

# Athena 中关于 UDF 的视频
<a name="udf-videos"></a>

观看以下视频，了解有关在 Athena 中使用 UDF 的更多信息。

**视频：在 Amazon Athena 中引入用户定义函数 (UDF)**  
以下视频展示了如何在 Amazon Athena 中使用 UDF 来编辑敏感信息。

**注意**  
本视频中的语法是预发行的，但概念是相同的。在没有 `AmazonAthenaPreviewFunctionality` 工作组的情况下使用 Athena。

[![AWS Videos](http://img.youtube.com/vi/https://www.youtube.com/embed/AxJ6jP4Pfmo/0.jpg)](http://www.youtube.com/watch?v=https://www.youtube.com/embed/AxJ6jP4Pfmo)


**视频：使用 Amazon Athena 中的 SQL 查询翻译、分析和编辑文本字段**  
以下视频展示了如何在 Amazon Athena 中使用 UDF 与其他 AWS 服务 相结合来翻译和分析文本。

**注意**  
本视频中的语法是预发行的，但概念是相同的。有关正确的语法，请参阅相关博客文章：*AWS Machine Learning 博客*中的[使用 Amazon Athena、Amazon Translate 和 Amazon Comprehend 的 SQL 函数翻译、编辑和分析文本](https://aws.amazon.com/blogs/machine-learning/translate-and-analyze-text-using-sql-functions-with-amazon-athena-amazon-translate-and-amazon-comprehend/)。

[![AWS Videos](http://img.youtube.com/vi/https://www.youtube.com/embed/Od7rXG-WMO4/0.jpg)](http://www.youtube.com/watch?v=https://www.youtube.com/embed/Od7rXG-WMO4)


# 注意事项和限制
<a name="udf-considerations-limitations"></a>

在 Athena 中使用用户定义函数（UDF）时，请考虑以下几点。
+ **内置 Athena 函数** – Athena 中的内置函数旨在实现高性能。我们建议尽可能使用内置函数而不是 UDF。有关内置函数的更多信息，请参阅 [Amazon Athena 中的函数](functions.md)。
+ **仅标量 UDF** – Athena 仅支持标量 UDF，它将一次处理一行并返回单个列值。每次调用 Lambda 时，Athena 都会将行批量（可能并行）传递给 UDF。在设计 UDF 和查询时，请注意此处理可能对网络流量产生的潜在影响。
+ **UDF 处理函数使用缩写格式**– 对于 UDF 函数使用缩写格式（而非完整格式）（例如，使用 `package.Class` 而非 `package.Class::method`）。
+ **UDF 方法必须为小写**– UDF 方法必须为小写字母；不允许使用骆驼大小写。
+ **UDF 方法需要参数** - UDF 方法必须至少有一个输入参数。尝试调用未定义输入参数的 UDF 会导致运行时异常。UDF 旨在对数据记录执行函数，但是没有参数的 UDF 不接受任何数据，因此会出现异常。
+ **Java 运行时支持**：目前，Athena UDF 支持用于 Lambda 的 Java 8、Java 11 和 Java 17 运行时。有关更多信息，请参阅《*AWS Lambda 开发人员指南*》中的[使用 Jav 构建 Lambda 函数](https://docs.aws.amazon.com/lambda/latest/dg/lambda-java.html)。
**注意**  
 对于 Java 17，您必须在 Lambda 中将 `JAVA_TOOL_OPTIONS` 环境变量的值设置为 `--add-opens=java.base/java.nio=ALL-UNNAMED`。
+ **IAM 权限** – 要在 Athena 中创建并运行 UDF 查询语句，则除 Athena 函数之外，还必须允许运行查询的 IAM 委托人执行操作。有关更多信息，请参阅 [允许访问 Athena UDF：策略示例](udf-iam-access.md)。
+ **Lambda 配额** – Lambda 配额适用于 UDF。有关更多信息，请参阅《*AWS Lambda 开发人员指南*》中的 [Lambda 配额](https://docs.aws.amazon.com/lambda/latest/dg/limits.html)。
+ **行级别筛选** - UDF 不支持 Lake Formation 行级别筛选。
+ **视图** – 您不能将视图与 UDF 同时使用。
+ **已知问题** – 有关已知问题的最新列表，请参阅 GitHub 的 awslabs/aws-athena-query-federation 部分中的 [Limitations and issues](https://github.com/awslabs/aws-athena-query-federation/wiki/Limitations_And_Issues)（限制和问题）。

# 使用 UDF 查询语法进行查询
<a name="udf-query-syntax"></a>

`USING EXTERNAL FUNCTION` 子句指定可由查询中的后续 `SELECT` 语句引用的 UDF 或多个 UDF。您需要 UDF 的方法名称和托管 UDF 的 Lambda 函数的名称。您可以使用 Lambda ARN 代替 Lambda 函数名称。在跨账户场景中，需要提供 Lambda ARN。

## 摘要
<a name="udf-synopsis"></a>

```
USING EXTERNAL FUNCTION UDF_name(variable1 data_type[, variable2 data_type][,...])
RETURNS data_type
LAMBDA 'lambda_function_name_or_ARN'
[, EXTERNAL FUNCTION UDF_name2(variable1 data_type[, variable2 data_type][,...]) 
RETURNS data_type 
LAMBDA 'lambda_function_name_or_ARN'[,...]]
SELECT  [...] UDF_name(expression) [, UDF_name2(expression)] [...]
```

## 参数
<a name="udf-parameters"></a>

**USING EXTERNAL FUNCTION *UDF\$1name*(*variable1**data\$1type*[, *variable2**data\$1type*][,...])**  
*UDF\$1name* 指定 UDF 的名称，该名称必须与引用的 Lambda 函数中的 Java 方法对应。每个 *variable data\$1type* 指定一个命名变量，且其相应的数据类型应可为 UDF 接受作为输入。*data\$1type* 必须是下表中列出的受支持 Athena 数据类型之一，并映射到相应的 Java 数据类型。      
[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/athena/latest/ug/udf-query-syntax.html)

**RETURNS *data\$1type***  
`data_type` 指定 UDF 作为输出返回的 SQL 数据类型。上表中列出的 Athena 数据类型均受支持。对于 `DECIMAL` 数据类型，请使用语法 `RETURNS DECIMAL(precision, scale)`，其中 *precision*（精度）和 *scale*（小数位数）是整数。

**LAMBDA '*lambda\$1function*'**  
*lambda\$1function* 指定运行 UDF 时要调用的 Lambda 函数的名称。

**SELECT [...]*UDF\$1name*(*expression*) [...]**  
`SELECT` 查询会将值传递给 UDF 并返回结果。*UDF\$1name* 指定要使用的 UDF，后跟一个*表达式*，该表达式将进行估算以传递值。传递和返回的值必须与 `USING EXTERNAL FUNCTION` 子句中为 UDF 指定的相应数据类型匹配。

### 示例
<a name="udf-examples"></a>

如需获取 GitHub 上基于[AthenaUDFHandler.java](https://github.com/awslabs/aws-athena-query-federation/blob/master/athena-udfs/src/main/java/com/amazonaws/athena/connectors/udfs/AthenaUDFHandler.java) 代码的示例查询，请参阅 GitHub 的 [Amazon Athena UDF connector](https://github.com/awslabs/aws-athena-query-federation/tree/master/athena-udfs)（Amazon Athena UDF 连接器）页面。

# 使用 Lambda 创建和部署 UDF
<a name="udf-creating-and-deploying"></a>

要创建自定义 UDF，可以通过扩展 `UserDefinedFunctionHandler` 类来创建新的 Java 类。开发工具包中的 [UserDefinedFunctionHandler.java](https://github.com/awslabs/aws-athena-query-federation/blob/master/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/handlers/UserDefinedFunctionHandler.java) 的源代码在 GitHub 上的 awslabs/aws-athena-query-federation/athena-federation-sdk [存储库](https://github.com/awslabs/aws-athena-query-federation/tree/master/athena-federation-sdk)中提供，其中还提供有[示例 UDF 实施](https://github.com/awslabs/aws-athena-query-federation/tree/master/athena-udfs)，您可以对其进行检查和修改，以创建自定义 UDF。

本节中的步骤演示从命令行使用 [Apache Maven](https://maven.apache.org/index.html) 编写和构建自定义 UDF Jar 文件和部署。

执行以下步骤以使用 Maven 为 Athena 创建自定义 UDF

1. [克隆开发工具包并准备开发环境](#udf-create-install-sdk-prep-environment)

1. [创建您的 Maven 项目](#create-maven-project)

1. [将依赖项和插件添加到您的 Maven 项目中](#udf-add-maven-dependencies)

1. [为 UDF 编写 Java 代码](#udf-write-java)

1. [构建 JAR 文件](#udf-create-package-jar)

1. [将 JAR 部署到 AWS Lambda](#udf-create-deploy)

## 克隆开发工具包并准备开发环境
<a name="udf-create-install-sdk-prep-environment"></a>

在开始之前，请确保使用 `sudo yum install git -y` 在您的系统上安装 git。

**安装 AWS 查询联合开发工具包**
+ 在命令行输入以下内容以克隆软件开发工具包存储库。此存储库包括软件开发工具包、示例和一套数据源连接器。有关数据源连接器的更多信息，请参阅[使用 Amazon Athena 联合查询](federated-queries.md)。

  ```
  git clone https://github.com/awslabs/aws-athena-query-federation.git
  ```

**此程序的安装先决条件**

如果您正在使用已安装 Apache Maven、AWS CLI 和 AWS Serverless Application Model 构建工具的开发计算机，则可以跳过此步骤。

1. 从克隆时创建的 `aws-athena-query-federation` 目录的根目录下，运行准备开发环境的 [prepare\$1dev\$1env.sh](https://github.com/awslabs/aws-athena-query-federation/blob/master/tools/prepare_dev_env.sh) 脚本。

1. 更新您的 shell 以获取在安装过程中创建的新变量或重新启动终端会话。

   ```
   source ~/.profile
   ```
**重要**  
如果您跳过此步骤，稍后将收到关于 AWS CLI 或 AWS SAM 构建工具无法发布 Lambda 函数的错误。

## 创建您的 Maven 项目
<a name="create-maven-project"></a>

运行以下命令以创建您的 Maven 项目。将 *groupId* 替换为组织的唯一 ID，并将 *my-athena-udf* 替换为您的应用程序的名称。有关详细信息，请参阅 Apache Maven 文档中的[如何创建我的第一个 Maven 项目？](https://maven.apache.org/guides/getting-started/index.html#How_do_I_make_my_first_Maven_project)。

```
mvn -B archetype:generate \
-DarchetypeGroupId=org.apache.maven.archetypes \
-DgroupId=groupId \
-DartifactId=my-athena-udfs
```

## 将依赖项和插件添加到您的 Maven 项目中
<a name="udf-add-maven-dependencies"></a>

将以下配置添加到 Maven 项目 `pom.xml` 文件中。要查看示例，请参阅 GitHub 中的 [pom.xml](https://github.com/awslabs/aws-athena-query-federation/blob/master/athena-udfs/pom.xml) 文件。

```
<properties>
    <aws-athena-federation-sdk.version>2022.47.1</aws-athena-federation-sdk.version>
</properties>

<dependencies>
    <dependency>
        <groupId>com.amazonaws</groupId>
        <artifactId>aws-athena-federation-sdk</artifactId>
        <version>${aws-athena-federation-sdk.version}</version>
    </dependency>
</dependencies>
    
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-shade-plugin</artifactId>
            <version>3.2.1</version>
            <configuration>
                <createDependencyReducedPom>false</createDependencyReducedPom>
                <filters>
                    <filter>
                        <artifact>*:*</artifact>
                        <excludes>
                            <exclude>META-INF/*.SF</exclude>
                            <exclude>META-INF/*.DSA</exclude>
                            <exclude>META-INF/*.RSA</exclude>
                        </excludes>
                    </filter>
                </filters>
            </configuration>
            <executions>
                <execution>
                    <phase>package</phase>
                    <goals>
                        <goal>shade</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>
```

## 为 UDF 编写 Java 代码
<a name="udf-write-java"></a>

通过扩展 [UserDefinedFunctionHandler.java](https://github.com/awslabs/aws-athena-query-federation/blob/master/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/handlers/UserDefinedFunctionHandler.java) 来创建一个新类。在类中编写您的 UDF。

在以下示例中，已在 `MyUserDefinedFunctions` 类中创建了两个用于 UDF、`compress()` 和 `decompress()` 的 Java 方法。

```
*package *com.mycompany.athena.udfs;

public class MyUserDefinedFunctions
        extends UserDefinedFunctionHandler
{
    private static final String SOURCE_TYPE = "MyCompany";

    public MyUserDefinedFunctions()
    {
        super(SOURCE_TYPE);
    }

    /**
     * Compresses a valid UTF-8 String using the zlib compression library.
     * Encodes bytes with Base64 encoding scheme.
     *
     * @param input the String to be compressed
     * @return the compressed String
     */
    public String compress(String input)
    {
        byte[] inputBytes = input.getBytes(StandardCharsets.UTF_8);

        // create compressor
        Deflater compressor = new Deflater();
        compressor.setInput(inputBytes);
        compressor.finish();

        // compress bytes to output stream
        byte[] buffer = new byte[4096];
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(inputBytes.length);
        while (!compressor.finished()) {
            int bytes = compressor.deflate(buffer);
            byteArrayOutputStream.write(buffer, 0, bytes);
        }

        try {
            byteArrayOutputStream.close();
        }
        catch (IOException e) {
            throw new RuntimeException("Failed to close ByteArrayOutputStream", e);
        }

        // return encoded string
        byte[] compressedBytes = byteArrayOutputStream.toByteArray();
        return Base64.getEncoder().encodeToString(compressedBytes);
    }

    /**
     * Decompresses a valid String that has been compressed using the zlib compression library.
     * Decodes bytes with Base64 decoding scheme.
     *
     * @param input the String to be decompressed
     * @return the decompressed String
     */
    public String decompress(String input)
    {
        byte[] inputBytes = Base64.getDecoder().decode((input));

        // create decompressor
        Inflater decompressor = new Inflater();
        decompressor.setInput(inputBytes, 0, inputBytes.length);

        // decompress bytes to output stream
        byte[] buffer = new byte[4096];
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(inputBytes.length);
        try {
            while (!decompressor.finished()) {
                int bytes = decompressor.inflate(buffer);
                if (bytes == 0 && decompressor.needsInput()) {
                    throw new DataFormatException("Input is truncated");
                }
                byteArrayOutputStream.write(buffer, 0, bytes);
            }
        }
        catch (DataFormatException e) {
            throw new RuntimeException("Failed to decompress string", e);
        }

        try {
            byteArrayOutputStream.close();
        }
        catch (IOException e) {
            throw new RuntimeException("Failed to close ByteArrayOutputStream", e);
        }

        // return decoded string
        byte[] decompressedBytes = byteArrayOutputStream.toByteArray();
        return new String(decompressedBytes, StandardCharsets.UTF_8);
    }
}
```

## 构建 JAR 文件
<a name="udf-create-package-jar"></a>

运行 `mvn clean install` 以构建您的项目。成功构建后，将在名为 `artifactId-version.jar` 的项目的 `target` 文件夹中创建一个 JAR 文件，其中 *artifactId* 是您在 Maven 项目中提供的名称，例如 `my-athena-udfs`。

## 将 JAR 部署到 AWS Lambda
<a name="udf-create-deploy"></a>

有两个选项可以将代码部署到 Lambda：
+ 使用 AWS Serverless Application Repository 部署（推荐）
+ 从 JAR 文件创建 Lambda 函数

### 选项 1：部署到 AWS Serverless Application Repository
<a name="udf-create-deploy-sar"></a>

将 JAR 文件部署到 AWS Serverless Application Repository 时，您将创建一个代表应用程序体系结构的 AWS SAM 模板 YAML 文件。然后，您可以指定此 YAML 文件和一个 Amazon S3 存储桶，在其中上载应用程序的构件并使其对 AWS Serverless Application Repository 可用。下面的过程使用 [publish.sh](https://github.com/awslabs/aws-athena-query-federation/blob/master/tools/publish.sh) 脚本，该脚本位于您之前克隆的 Athena Query Federation SDK 的 `athena-query-federation/tools` 目录中。

有关更多信息和要求，请参阅《**AWS Serverless Application Repository 开发人员指南》中的 [发布应用程序](https://docs.aws.amazon.com/serverlessrepo/latest/devguide/serverlessrepo-publishing-applications.html)、《**AWS Serverless Application Model 开发人员指南》中的 [AWS SAM 模板概念](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-template-basics.html)和 [使用 AWS SAM CLI 发布无服务器应用程序](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-template-publishing-applications.html)。

以下示例演示了 YAML 文件中的参数。将类似的参数添加到 YAML 文件并将其保存在项目目录中。有关完整示例，请参阅 GitHub 中的 [athena-udf.yaml](https://github.com/awslabs/aws-athena-query-federation/blob/master/athena-udfs/athena-udfs.yaml)。

```
Transform: 'AWS::Serverless-2016-10-31'
Metadata:
  'AWS::ServerlessRepo::Application':
    Name: MyApplicationName
    Description: 'The description I write for my application'
    Author: 'Author Name'
    Labels:
      - athena-federation
    SemanticVersion: 1.0.0
Parameters:
  LambdaFunctionName:
    Description: 'The name of the Lambda function that will contain your UDFs.'
    Type: String
  LambdaTimeout:
    Description: 'Maximum Lambda invocation runtime in seconds. (min 1 - 900 max)'
    Default: 900
    Type: Number
  LambdaMemory:
    Description: 'Lambda memory in MB (min 128 - 3008 max).'
    Default: 3008
    Type: Number
Resources:
  ConnectorConfig:
    Type: 'AWS::Serverless::Function'
    Properties:
      FunctionName: !Ref LambdaFunctionName
      Handler: "full.path.to.your.handler. For example, com.amazonaws.athena.connectors.udfs.MyUDFHandler"
      CodeUri: "Relative path to your JAR file. For example, ./target/athena-udfs-1.0.jar"
      Description: "My description of the UDFs that this Lambda function enables."
      Runtime: java8
      Timeout: !Ref LambdaTimeout
      MemorySize: !Ref LambdaMemory
```

将 `publish.sh` 脚本复制到保存 YAML 文件的项目目录中，然后运行以下命令：

```
./publish.sh MyS3Location MyYamlFile
```

例如，如果您的存储桶位置为 `s3://amzn-s3-demo-bucket/mysarapps/athenaudf` 并且您的 YAML 文件已另存为 `my-athena-udfs.yaml`：

```
./publish.sh amzn-s3-demo-bucket/mysarapps/athenaudf my-athena-udfs
```

**创建 Lambda 函数**

1. 在以下位置打开 Lambda 控制台：[https://console.aws.amazon.com/lambda/](https://console.aws.amazon.com/lambda/)，选择 **Create function**（创建函数），然后选择 **Browse serverless app repository**（浏览无服务器应用程序存储库）

1. 选择 **Private applications (私有应用程序)**，在列表中找到您的应用程序，或使用关键词搜索它，然后选择它。

1. 查看并提供应用程序详细信息，然后选择 **Deploy (部署)**。

   您现在可以使用在 Lambda 函数 JAR 文件中定义的方法名称作为 Athena 中的 UDF。

### 选项 2：直接创建 Lambda 函数
<a name="udf-create-deploy-lambda"></a>

您也可以直接使用控制台或 AWS CLI 创建 Lambda 函数。以下示例演示如何使用 `create-function` Lambda CLI 命令。

```
aws lambda create-function \
 --function-name MyLambdaFunctionName \
 --runtime java8 \
 --role arn:aws:iam::1234567890123:role/my_lambda_role \
 --handler com.mycompany.athena.udfs.MyUserDefinedFunctions \
 --timeout 900 \
 --zip-file fileb://./target/my-athena-udfs-1.0-SNAPSHOT.jar
```

# 跨区域查询
<a name="querying-across-regions"></a>

Athena 支持在 AWS 区域 中查询 Amazon S3 数据，此区域与您使用 Athena 的区域不同。若移动数据不可行或没有权限时，或者您想跨多个区域查询数据，则可以选择跨区域查询。即使 Athena 在特定区域不可用，也可以从另一个可使用 Athena 的区域查询该地区的数据。

若要查询某个区域中的数据，即使 Amazon S3 数据不属于您的账户，也必须在该区域启用您的账户。对于某些区域 [例如美国东部（俄亥俄）]，创建账户时会自动启用您对该区域的访问权限。其他区域要求您的账户在使用之前必须“选择加入”该区域。有关要求选择加入的区域列表，请参阅《*Amazon EC2 用户指南*》中的[可用区域](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-regions-availability-zones.html#concepts-available-regions)。有关选择加入区域的特定说明，请参阅 *Amazon Web Services 一般参考* 中的[管理 AWS 区域](https://docs.aws.amazon.com/general/latest/gr/rande-manage.html)。

## 注意事项和限制
<a name="querying-across-regions-considerations-and-limitations"></a>
+ **数据访问权限** – 若要跨地区成功查询 Athena 的 Amazon S3 数据，您的账户必须具有读取数据的权限。若您要查询的数据属于另一个账户，则此其他账户必须授予您对包含该数据的 Amazon S3 位置的访问权限。
+ **数据传输费** – 跨区域查询可能会收取 Amazon S3 数据传输费。运行查询可能导致传输的数据超过数据集的大小。我们建议您首先测试对数据子集的查询，然后查看 [AWS Cost Explorer](https://aws.amazon.com/aws-cost-management/aws-cost-explorer/) 中的成本。
+ **AWS Glue** – 您可以跨区域使用 AWS Glue。跨区域 AWS Glue 流量可能收取额外费用。有关更多信息，请参阅 [AWS Glue 大数据博客](https://aws.amazon.com/blogs/big-data/create-cross-account-and-cross-region-aws-glue-connections/)中的*创建跨账户和跨区域 AWS 连接*。
+ **Amazon S3 加密选项** – SSE-S3 和 SSE-KMS 加密选项支持跨区域查询；CSE-KMS 不支持跨区域查询。有关更多信息，请参阅 [支持的 Amazon S3 加密选项](encryption.md#encryption-options-S3-and-Athena)。
+ **联合查询** - 不支持跨 AWS 区域 使用联合查询。

只要满足上述条件，您就可以创建一个指向自己指定的 `LOCATION` 值的 Athena 表，并透明地查询数据。不需要特殊语法。有关创建 Athena 表的信息，请参阅[在 Athena 中创建表](creating-tables.md)。

# 查询 AWS Glue Data Catalog
<a name="querying-glue-catalog"></a>

由于许多 AWS 服务 都将 AWS Glue Data Catalog 用作其中央元数据存储库，因此您可能需要查询 Data Catalog 元数据。为此，您可以在 Athena 中使用 SQL 查询。您可以使用 Athena 查询 AWS Glue 目录元数据，如数据库、表、分区和列等。

要获取 AWS Glue Catalog 元数据，请查询 Athena 后端上的 `information_schema` 数据库。本主题中的示例查询显示如何使用 Athena 查询 AWS Glue Catalog 元数据以搜索常用案例。

## 注意事项和限制
<a name="querying-glue-catalog-considerations-limitations"></a>
+ 您可以不查询 `information_schema` 数据库，而是使用单独的 Apache Hive [DDL 命令](ddl-reference.md)从 Athena 中提取特定数据库、表、视图、分区和列的元数据信息。但输出将为非表格格式。
+ 如果您的 AWS Glue 元数据较少或中等，则查询 `information_schema` 的性能会最佳。如果您有大量的元数据，则可能会出现错误。
+ 不能使用 `CREATE VIEW` 在 `information_schema` 数据库上创建视图。

**Topics**
+ [注意事项和限制](#querying-glue-catalog-considerations-limitations)
+ [列出数据库并搜索指定的数据库](querying-glue-catalog-querying-available-databases-including-rdbms.md)
+ [列出指定数据库中的表并按名称搜索表](querying-glue-catalog-listing-tables.md)
+ [列出特定表的分区](querying-glue-catalog-listing-partitions.md)
+ [列出或搜索指定表或视图的列](querying-glue-catalog-listing-columns.md)
+ [列出特定的表的共同列](querying-glue-catalog-listing-columns-in-common.md)
+ [列出所有表的所有列](querying-glue-catalog-listing-all-columns-for-all-tables.md)

# 列出数据库并搜索指定的数据库
<a name="querying-glue-catalog-querying-available-databases-including-rdbms"></a>

本节中的示例演示如何按模式名称列出元数据中的数据库。

**Example – 列出数据库**  
以下示例查询列出 `information_schema.schemata` 表中的数据库。  

```
SELECT schema_name
FROM   information_schema.schemata
LIMIT  10;
```
下表显示了示例结果。  


****  

|  |  | 
| --- |--- |
| 6 | alb-databas1 | 
| 7 | alb\$1original\$1cust | 
| 8 | alblogsdatabase | 
| 9 | athena\$1db\$1test | 
| 10 | athena\$1ddl\$1db | 

**Example – 搜索指定的数据库**  
在以下示例查询中，`rdspostgresql` 是示例数据库。  

```
SELECT schema_name
FROM   information_schema.schemata
WHERE  schema_name = 'rdspostgresql'
```
下表显示了示例结果。  


****  

|  | schema\$1name | 
| --- | --- | 
| 1 | rdspostgresql | 

# 列出指定数据库中的表并按名称搜索表
<a name="querying-glue-catalog-listing-tables"></a>

要列出表的元数据，您可以按表架构或表名进行查询。

**Example – 按架构列出表**  
以下查询列出了使用 `rdspostgresql` 表架构的表。  

```
SELECT table_schema,
       table_name,
       table_type
FROM   information_schema.tables
WHERE  table_schema = 'rdspostgresql'
```
下图显示了一个示例结果。  


****  

|  | table\$1schema | table\$1name | table\$1type | 
| --- | --- | --- | --- | 
| 1 | rdspostgresql | rdspostgresqldb1\$1public\$1account | BASE TABLE | 

**Example – 按名称搜索表**  
以下查询获取表 `athena1` 的元数据信息。  

```
SELECT table_schema,
       table_name,
       table_type
FROM   information_schema.tables
WHERE  table_name = 'athena1'
```
下图显示了一个示例结果。  


****  

|  | table\$1schema | table\$1name | table\$1type | 
| --- | --- | --- | --- | 
| 1 | 默认 | athena1 | BASE TABLE | 

# 列出特定表的分区
<a name="querying-glue-catalog-listing-partitions"></a>

您可以使用 `SHOW PARTITIONS table_name` 以列出指定表的分区，如以下示例所示。

```
SHOW PARTITIONS cloudtrail_logs_test2
```

您还可以使用 `$partitions` 元数据查询列出特定表的分区号和分区值。

**Example – 使用 \$1partitions 语法查询表的分区**  
以下示例查询使用 `$partitions` 语法列出了表 `cloudtrail_logs_test2` 的分区。  

```
SELECT * FROM default."cloudtrail_logs_test2$partitions" ORDER BY partition_number
```
下表显示了示例结果。  


****  

|  | table\$1catalog | table\$1schema | table\$1name | Year | Month | 天 | 
| --- | --- | --- | --- | --- | --- | --- | 
| 1 | awsdatacatalog | default | cloudtrail\$1logs\$1test2 | 2020 | 08 | 10 | 
| 2 | awsdatacatalog | default | cloudtrail\$1logs\$1test2 | 2020 | 08 | 11 | 
| 3 | awsdatacatalog | default | cloudtrail\$1logs\$1test2 | 2020 | 08 | 12 | 

# 列出或搜索指定表或视图的列
<a name="querying-glue-catalog-listing-columns"></a>

您可以列出表的所有列、视图的所有列，或者在指定的数据库和表中按名称搜索列。

要列出列，请使用 `SELECT *` 查询。在 `FROM` 子句中，指定 `information_schema.columns`。在 `WHERE` 子句中，使用 `table_schema='database_name'` 以指定数据库，使用 `table_name = 'table_name'` 指定包含要列出的列的表或视图。

**Example – 列出指定表的所有列**  
以下示例查询列出了 `rdspostgresqldb1_public_account` 表的所有列。  

```
SELECT *
FROM   information_schema.columns
WHERE  table_schema = 'rdspostgresql'
       AND table_name = 'rdspostgresqldb1_public_account'
```
下表显示了示例结果。  


****  

|  | table\$1catalog | table\$1schema | table\$1name | column\$1name | ordinal\$1position | column\$1default | is\$1nullable | data\$1type | comment | extra\$1info | 
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | 
| 1 | awsdatacatalog | rdspostgresql | rdspostgresqldb1\$1public\$1account | password | 1 |  | 是 | varchar |  |  | 
| 2 | awsdatacatalog | rdspostgresql | rdspostgresqldb1\$1public\$1account | user\$1id | 2 |  | 是 | 整数 |  |  | 
| 3 | awsdatacatalog | rdspostgresql | rdspostgresqldb1\$1public\$1account | created\$1on | 3 |  | 是 | timestamp |  |  | 
| 4 | awsdatacatalog | rdspostgresql | rdspostgresqldb1\$1public\$1account | last\$1login | 4 |  | 是 | timestamp |  |  | 
| 5 | awsdatacatalog | rdspostgresql | rdspostgresqldb1\$1public\$1account | 电子邮件 | 5 |  | 是 | varchar |  |  | 
| 6 | awsdatacatalog | rdspostgresql | rdspostgresqldb1\$1public\$1account | username | 6 |  | 是 | varchar |  |  | 

**Example – 列出指定视图的列**  
以下示例查询列出了 `default` 数据库中用于 `arrayview` 视图的所有列。  

```
SELECT *
FROM   information_schema.columns
WHERE  table_schema = 'default'
       AND table_name = 'arrayview'
```
下表显示了示例结果。  


****  

|  | table\$1catalog | table\$1schema | table\$1name | column\$1name | ordinal\$1position | column\$1default | is\$1nullable | data\$1type | comment | extra\$1info | 
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | 
| 1 | awsdatacatalog | default | arrayview | searchdate | 1 |  | 是 | varchar |  |  | 
| 2 | awsdatacatalog | default | arrayview | sid | 2 |  | 是 | varchar |  |  | 
| 3 | awsdatacatalog | default | arrayview | btid | 3 |  | 是 | varchar |  |  | 
| 4 | awsdatacatalog | default | arrayview | p | 4 |  | 是 | varchar |  |  | 
| 5 | awsdatacatalog | default | arrayview | infantprice | 5 |  | 是 | varchar |  |  | 
| 6 | awsdatacatalog | default | arrayview | sump | 6 |  | 是 | varchar |  |  | 
| 7 | awsdatacatalog | default | arrayview | journeymaparray | 7 |  | 是 | array(varchar) |  |  | 

**Example – 在指定数据库和表中按名称搜索列**  
以下示例查询在 `default` 数据库的 `arrayview` 视图中搜索 `sid` 列的元数据。  

```
SELECT *
FROM   information_schema.columns
WHERE  table_schema = 'default'
       AND table_name = 'arrayview' 
       AND column_name='sid'
```
下图显示了一个示例结果。  


****  

|  | table\$1catalog | table\$1schema | table\$1name | column\$1name | ordinal\$1position | column\$1default | is\$1nullable | data\$1type | comment | extra\$1info | 
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | 
| 1 | awsdatacatalog | default | arrayview | sid | 2 |  | 是 | varchar |  |  | 

# 列出特定的表的共同列
<a name="querying-glue-catalog-listing-columns-in-common"></a>

可以列出数据库中的特定的表的共同列。
+ 使用语法 `SELECT column_name FROM information_schema.columns`。
+ 对于 `WHERE` 子句，请使用语法 `WHERE table_name IN ('table1', 'table2')`。

**Example – 列出同一个数据库中的两个表的共同列**  
如下示例查询列出了表 `table1` 和表 `table2` 的共同列。  

```
SELECT column_name
FROM information_schema.columns
WHERE table_name IN ('table1', 'table2')
GROUP BY column_name
HAVING COUNT(*) > 1;
```

# 列出所有表的所有列
<a name="querying-glue-catalog-listing-all-columns-for-all-tables"></a>

您可以列出 `AwsDataCatalog` 中所有表的所有列，或者 `AwsDataCatalog` 中特定数据库中所有表的所有列。
+ 要列出 `AwsDataCatalog` 中所有数据库的所有列，请使用查询 `SELECT * FROM information_schema.columns`。
+ 要将结果限制在特定数据库中，请使用 `WHERE` 子句中的 `table_schema='database_name'`。

**Example — 列出特定数据库中所有表的所有列**  
以下示例查询列出了数据库 `webdata` 中所有表的所有列。  

```
SELECT * FROM information_schema.columns WHERE table_schema = 'webdata'            
```

# 查询 AWS 服务日志
<a name="querying-aws-service-logs"></a>

本部分包含使用 Amazon Athena 查询常见数据集的几个过程，例如 AWS CloudTrail 日志、Amazon CloudFront 日志、经典负载均衡器日志、Application Load Balancer 日志、Amazon VPC 流日志和网络负载均衡器日志。

本节中的任务使用 Athena 控制台，但您也可以使用其他工具，例如 [Athena JDBC 驱动程序](connect-with-jdbc.md)、[AWS CLI](https://docs.aws.amazon.com/cli/latest/reference/athena/) 或 [Amazon Athena API 参考](https://docs.aws.amazon.com/athena/latest/APIReference/)。

有关使用 AWS CloudFormation 自动创建 AWS 服务 日志表、分区和 Athena 中示例查询的信息，请参阅 AWS 大数据博客中的《[使用 Amazon Athena 实现自动化 AWS 服务 日志表创建并使用 Amazon Athena 进行查询](https://aws.amazon.com/blogs/big-data/automating-aws-service-logs-table-creation-and-querying-them-with-amazon-athena/)》。要了解如何将 Python 库用于 AWS Glue 以创建一个用于处理 AWS 服务 日志的常用框架，并在 Athena 中查询它们，请参阅 [使用 Amazon Athena 轻松查询 AWS 服务 日志](https://aws.amazon.com/blogs/big-data/easily-query-aws-service-logs-using-amazon-athena/)。

本节中的主题假定您已配置适当权限来访问 Athena 和要查询的数据所在的 Amazon S3 存储桶。有关更多信息，请参阅[设置、管理和编程访问](setting-up.md) 和[开始使用](getting-started.md)。

**Topics**
+ [应用程序负载均衡器](application-load-balancer-logs.md)
+ [Elastic Load Balancing](elasticloadbalancer-classic-logs.md)
+ [CloudFront](cloudfront-logs.md)
+ [CloudTrail](cloudtrail-logs.md)
+ [Amazon EMR](emr-logs.md)
+ [Global Accelerator](querying-global-accelerator-flow-logs.md)
+ [GuardDuty](querying-guardduty.md)
+ [Network Firewall](querying-network-firewall-logs.md)
+ [网络负载均衡器](networkloadbalancer-classic-logs.md)
+ [Route 53](querying-r53-resolver-logs.md)
+ [Amazon SES](querying-ses-logs.md)
+ [Amazon VPC](vpc-flow-logs.md)
+ [AWS WAF](waf-logs.md)

# 查询应用程序负载均衡器日志
<a name="application-load-balancer-logs"></a>

Application Load Balancer 是 Elastic Load Balancing 的负载均衡选项，它允许使用容器在微服务部署中实现流量分配。通过查询 Application Load Balancer 日志，您可以查看进出 Elastic Load Balancing 实例和后端应用程序的流量来源、延迟和传输字节。有关更多信息，请参阅《User Guide for Application Load Balancers》**中的 [Access logs for your Application Load Balancer](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-access-logs.html) 和 [Connection logs for your Application Load Balancer](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-connection-logs.html)。

## 先决条件
<a name="application-load-balancer-logs-prerequisites"></a>
+ 启用[访问日志记录](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-access-logs.html)功能或[连接日志记录](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-connection-logs.html)功能，以便将应用程序负载均衡器日志保存到 Amazon S3 存储桶。
+ 用于保存您将为 Athena 创建的表的数据库。要创建数据库，你可以使用 Athena 或 AWS Glue 控制台。有关更多信息，请参阅本指南中的 [在 Athena 中创建数据库](creating-databases.md) 或《AWS Glue 开发人员指南**》中的 [通过 AWS Glue 控制台使用数据库](https://docs.aws.amazon.com/glue/latest/dg/console-databases.html)。

**Topics**
+ [先决条件](#application-load-balancer-logs-prerequisites)
+ [为 ALB 访问日志创建表](create-alb-access-logs-table.md)
+ [使用分区投影功能在 Athena 中为 ALB 访问日志创建表](create-alb-access-logs-table-partition-projection.md)
+ [ALB 访问日志的示例查询](query-alb-access-logs-examples.md)
+ [为 ALB 连接日志创建表](create-alb-connection-logs-table.md)
+ [使用分区投影功能在 Athena 中为 ALB 连接日志创建表](create-alb-connection-logs-table-partition-projection.md)
+ [ALB 连接日志的示例查询](query-alb-connection-logs-examples.md)
+ [其他资源](application-load-balancer-logs-additional-resources.md)

# 为 ALB 访问日志创建表
<a name="create-alb-access-logs-table"></a>

1. 将以下 `CREATE TABLE` 语句复制并粘贴到 Athena 控制台的查询编辑器中，然后根据您自己的日志条目要求进行必要的修改。有关 Amazon 控制台入门的更多信息，请参阅 [开始使用](getting-started.md)。将 `LOCATION` 子句中的路径替换为 Amazon S3 访问日志文件夹位置。有关访问日志文件位置的更多信息，请参阅《User Guide for Application Load Balancers》**中的 [Access log files](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-access-logs.html#access-log-file-format)。

   有关每个日志文件字段的信息，请参阅《*应用程序负载均衡器用户指南*》中的[访问日志条目](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-access-logs.html#access-log-entry-format)。
**注意**  
以下示例 `CREATE TABLE` 语句包括最近添加的 `classification`、`classification_reason` 和 `conn_trace_id`（“可追溯性 ID”或 TID）列。要为不包含这些条目的应用程序负载均衡器访问日志创建表，请从 `CREATE TABLE` 语句中删除相应的列，并相应地修改正则表达式。

   ```
   CREATE EXTERNAL TABLE IF NOT EXISTS alb_access_logs (
               type string,
               time string,
               elb string,
               client_ip string,
               client_port int,
               target_ip string,
               target_port int,
               request_processing_time double,
               target_processing_time double,
               response_processing_time double,
               elb_status_code int,
               target_status_code string,
               received_bytes bigint,
               sent_bytes bigint,
               request_verb string,
               request_url string,
               request_proto string,
               user_agent string,
               ssl_cipher string,
               ssl_protocol string,
               target_group_arn string,
               trace_id string,
               domain_name string,
               chosen_cert_arn string,
               matched_rule_priority string,
               request_creation_time string,
               actions_executed string,
               redirect_url string,
               lambda_error_reason string,
               target_port_list string,
               target_status_code_list string,
               classification string,
               classification_reason string,
               conn_trace_id string
               )
               ROW FORMAT SERDE 'org.apache.hadoop.hive.serde2.RegexSerDe'
               WITH SERDEPROPERTIES (
               'serialization.format' = '1',
               'input.regex' = 
           '([^ ]*) ([^ ]*) ([^ ]*) ([^ ]*):([0-9]*) ([^ ]*)[:-]([0-9]*) ([-.0-9]*) ([-.0-9]*) ([-.0-9]*) (|[-0-9]*) (-|[-0-9]*) ([-0-9]*) ([-0-9]*) \"([^ ]*) (.*) (- |[^ ]*)\" \"([^\"]*)\" ([A-Z0-9-_]+) ([A-Za-z0-9.-]*) ([^ ]*) \"([^\"]*)\" \"([^\"]*)\" \"([^\"]*)\" ([-.0-9]*) ([^ ]*) \"([^\"]*)\" \"([^\"]*)\" \"([^ ]*)\" \"([^\\s]+?)\" \"([^\\s]+)\" \"([^ ]*)\" \"([^ ]*)\" ?([^ ]*)? ?( .*)?'
               )
               LOCATION 's3://amzn-s3-demo-bucket/access-log-folder-path/'
   ```
**注意**  
建议始终保留 `input.regex` 参数末尾的模式 *`?( .*)?`*，以便在添加新的 ALB 日志字段时处理新来的日志条目。

1. 在 Athena 控制台中运行查询。查询完成后，Athena 将注册 `alb_access_logs` 表，使其中的数据可以供您发出查询。

# 使用分区投影功能在 Athena 中为 ALB 访问日志创建表
<a name="create-alb-access-logs-table-partition-projection"></a>

由于 ALB 访问日志具有一个可以预先指定其分区方案的已知结构，所以您可以使用 Athena 分区投影功能，以此来减少查询运行时间并自动管理分区。当添加新数据时，分区投影会自动添加新分区。这样就不必使用 `ALTER TABLE ADD PARTITION` 手动添加分区了。

从指定日期开始到当前日期为止，以下示例 `CREATE TABLE` 语句会自动在 ALB 访问日志上为单个 AWS 区域使用分区投影。该语句以上一部分中的示例为基础，但添加了 `PARTITIONED BY` 和 `TBLPROPERTIES` 子句以启用分区投影。在 `LOCATION` 和 `storage.location.template` 子句中，将占位符替换为标识 ALB 访问日志在 Amazon S3 存储桶中位置的值。有关访问日志文件位置的更多信息，请参阅《User Guide for Application Load Balancers》**中的 [Access log files](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-access-logs.html#access-log-file-format)。对于 `projection.day.range`，将 *2022*/*01*/*01* 替换为要使用的开始日期。成功运行查询后，您可以查询表。您无需运行 `ALTER TABLE ADD PARTITION` 来加载分区。有关每个日志文件字段的信息，请参阅 [Access log entries](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-access-logs.html#access-log-entry-format)。

```
CREATE EXTERNAL TABLE IF NOT EXISTS alb_access_logs (
            type string,
            time string,
            elb string,
            client_ip string,
            client_port int,
            target_ip string,
            target_port int,
            request_processing_time double,
            target_processing_time double,
            response_processing_time double,
            elb_status_code int,
            target_status_code string,
            received_bytes bigint,
            sent_bytes bigint,
            request_verb string,
            request_url string,
            request_proto string,
            user_agent string,
            ssl_cipher string,
            ssl_protocol string,
            target_group_arn string,
            trace_id string,
            domain_name string,
            chosen_cert_arn string,
            matched_rule_priority string,
            request_creation_time string,
            actions_executed string,
            redirect_url string,
            lambda_error_reason string,
            target_port_list string,
            target_status_code_list string,
            classification string,
            classification_reason string,
            conn_trace_id string
            )
            PARTITIONED BY
            (
             day STRING
            )
            ROW FORMAT SERDE 'org.apache.hadoop.hive.serde2.RegexSerDe'
            WITH SERDEPROPERTIES (
            'serialization.format' = '1',
            'input.regex' = 
        '([^ ]*) ([^ ]*) ([^ ]*) ([^ ]*):([0-9]*) ([^ ]*)[:-]([0-9]*) ([-.0-9]*) ([-.0-9]*) ([-.0-9]*) (|[-0-9]*) (-|[-0-9]*) ([-0-9]*) ([-0-9]*) \"([^ ]*) (.*) (- |[^ ]*)\" \"([^\"]*)\" ([A-Z0-9-_]+) ([A-Za-z0-9.-]*) ([^ ]*) \"([^\"]*)\" \"([^\"]*)\" \"([^\"]*)\" ([-.0-9]*) ([^ ]*) \"([^\"]*)\" \"([^\"]*)\" \"([^ ]*)\" \"([^\\s]+?)\" \"([^\\s]+)\" \"([^ ]*)\" \"([^ ]*)\" ?([^ ]*)? ?( .*)?'
            )
            LOCATION 's3://amzn-s3-demo-bucket/AWSLogs/<ACCOUNT-NUMBER>/elasticloadbalancing/<REGION>/'
            TBLPROPERTIES
            (
             "projection.enabled" = "true",
             "projection.day.type" = "date",
             "projection.day.range" = "2022/01/01,NOW",
             "projection.day.format" = "yyyy/MM/dd",
             "projection.day.interval" = "1",
             "projection.day.interval.unit" = "DAYS",
             "storage.location.template" = "s3://amzn-s3-demo-bucket/AWSLogs/<ACCOUNT-NUMBER>/elasticloadbalancing/<REGION>/${day}"
            )
```

更多有关分区投影的信息，请参阅 [将分区投影与 Amazon Athena 结合使用](partition-projection.md)。

**注意**  
建议 `input.regex` 参数末尾始终保留 *?( .\$1)?* 模式，以便在添加新的 ALB 日志字段时处理新来的日志条目。

# ALB 访问日志的示例查询
<a name="query-alb-access-logs-examples"></a>

以下查询计算按客户端 IP 地址分组的负载均衡器接收到的 HTTP GET 请求的数目：

```
SELECT COUNT(request_verb) AS
 count,
 request_verb,
 client_ip
FROM alb_access_logs
GROUP BY request_verb, client_ip
LIMIT 100;
```

另一个查询显示 Safari 浏览器用户访问的 URL：

```
SELECT request_url
FROM alb_access_logs
WHERE user_agent LIKE '%Safari%'
LIMIT 10;
```

以下查询显示了 ELB 状态代码值大于或等于 500 的记录。

```
SELECT * FROM alb_access_logs
WHERE elb_status_code >= 500
```

以下示例演示了如何根据 `datetime` 解析日志：

```
SELECT client_ip, sum(received_bytes) 
FROM alb_access_logs
WHERE parse_datetime(time,'yyyy-MM-dd''T''HH:mm:ss.SSSSSS''Z') 
     BETWEEN parse_datetime('2018-05-30-12:00:00','yyyy-MM-dd-HH:mm:ss') 
     AND parse_datetime('2018-05-31-00:00:00','yyyy-MM-dd-HH:mm:ss') 
GROUP BY client_ip;
```

以下查询会查询在指定日期内对所有 ALB 访问日志使用分区投影的表。

```
SELECT * 
FROM alb_access_logs 
WHERE day = '2022/02/12'
```

# 为 ALB 连接日志创建表
<a name="create-alb-connection-logs-table"></a>

1. 将以下示例 `CREATE TABLE` 语句复制并粘贴到 Athena 控制台的查询编辑器中，然后根据您自己的日志条目要求进行必要的修改。有关 Amazon 控制台入门的更多信息，请参阅 [开始使用](getting-started.md)。将 `LOCATION` 子句中的路径替换为 Amazon S3 连接日志文件夹位置。有关连接日志文件位置的更多信息，请参阅《User Guide for Application Load Balancers》**中的 [Connection log files](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-connection-logs.html#connection-log-file-format)。有关每个日志文件字段的信息，请参阅 [Connection log entries](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-connection-logs.html#connection-log-entry-format)。

   ```
   CREATE EXTERNAL TABLE IF NOT EXISTS alb_connection_logs (
            time string,
            client_ip string,
            client_port int,
            listener_port int,
            tls_protocol string,
            tls_cipher string,
            tls_handshake_latency double,
            leaf_client_cert_subject string,
            leaf_client_cert_validity string,
            leaf_client_cert_serial_number string,
            tls_verify_status string,
            conn_trace_id string
            ) 
            ROW FORMAT SERDE 'org.apache.hadoop.hive.serde2.RegexSerDe'
            WITH SERDEPROPERTIES (
            'serialization.format' = '1',
            'input.regex' =
             '([^ ]*) ([^ ]*) ([0-9]*) ([0-9]*) ([A-Za-z0-9.-]*) ([^ ]*) ([-.0-9]*) \"([^\"]*)\" ([^ ]*) ([^ ]*) ([^ ]*) ?([^ ]*)?( .*)?'
            )
            LOCATION 's3://amzn-s3-demo-bucket/connection-log-folder-path/'
   ```

1. 在 Athena 控制台中运行查询。查询完成后，Athena 将注册 `alb_connection_logs` 表，使其中的数据可以供您发出查询。

# 使用分区投影功能在 Athena 中为 ALB 连接日志创建表
<a name="create-alb-connection-logs-table-partition-projection"></a>

由于 ALB 日志具有一个可以预先指定其分区方案的已知结构，您可以使用 Athena 分区投影功能，以此来减少查询运行时间并自动管理分区。当添加新数据时，分区投影会自动添加新分区。这样就不必使用 `ALTER TABLE ADD PARTITION` 手动添加分区了。

从指定日期开始到当前日期为止，以下示例 `CREATE TABLE` 语句会自动在 ALB 连接日志上为单个 AWS 区域使用分区投影。该语句以上一部分中的示例为基础，但添加了 `PARTITIONED BY` 和 `TBLPROPERTIES` 子句以启用分区投影。在 `LOCATION` 和 `storage.location.template` 子句中，将占位符替换为标识 ALB 连接日志在 Amazon S3 存储桶中位置的值。有关连接日志文件位置的更多信息，请参阅《User Guide for Application Load Balancers》**中的 [Connection log files](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-connection-logs.html#connection-log-file-format)。对于 `projection.day.range`，将 *2023*/*01*/*01* 替换为要使用的开始日期。成功运行查询后，您可以查询表。您无需运行 `ALTER TABLE ADD PARTITION` 来加载分区。有关每个日志文件字段的信息，请参阅 [Connection log entries](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-connection-logs.html#connection-log-entry-format)。

```
CREATE EXTERNAL TABLE IF NOT EXISTS alb_connection_logs (
         time string,
         client_ip string,
         client_port int,
         listener_port int,
         tls_protocol string,
         tls_cipher string,
         tls_handshake_latency double,
         leaf_client_cert_subject string,
         leaf_client_cert_validity string,
         leaf_client_cert_serial_number string,
         tls_verify_status string,
         conn_trace_id string
         )
            PARTITIONED BY
            (
             day STRING
            )
            ROW FORMAT SERDE 'org.apache.hadoop.hive.serde2.RegexSerDe'
            WITH SERDEPROPERTIES (
            'serialization.format' = '1',
            'input.regex' =
             '([^ ]*) ([^ ]*) ([0-9]*) ([0-9]*) ([A-Za-z0-9.-]*) ([^ ]*) ([-.0-9]*) \"([^\"]*)\" ([^ ]*) ([^ ]*) ([^ ]*) ?([^ ]*)?( .*)?'
            )
            LOCATION 's3://amzn-s3-demo-bucket/AWSLogs/<ACCOUNT-NUMBER>/elasticloadbalancing/<REGION>/'
            TBLPROPERTIES
            (
             "projection.enabled" = "true",
             "projection.day.type" = "date",
             "projection.day.range" = "2023/01/01,NOW",
             "projection.day.format" = "yyyy/MM/dd",
             "projection.day.interval" = "1",
             "projection.day.interval.unit" = "DAYS",
             "storage.location.template" = "s3://amzn-s3-demo-bucket/AWSLogs/<ACCOUNT-NUMBER>/elasticloadbalancing/<REGION>/${day}"
            )
```

更多有关分区投影的信息，请参阅 [将分区投影与 Amazon Athena 结合使用](partition-projection.md)。

# ALB 连接日志的示例查询
<a name="query-alb-connection-logs-examples"></a>

以下查询对 `tls_verify_status` 值不为 `'Success'` 的情况进行计数，结果按客户端 IP 地址分组：

```
SELECT DISTINCT client_ip, count() AS count FROM alb_connection_logs
WHERE tls_verify_status != 'Success'
GROUP BY client_ip
ORDER BY count() DESC;
```

以下查询搜索指定时间范围内 `tls_handshake_latency` 值超过 2 秒的情况：

```
SELECT * FROM alb_connection_logs
WHERE 
  (
    parse_datetime(time, 'yyyy-MM-dd''T''HH:mm:ss.SSSSSS''Z') 
    BETWEEN 
    parse_datetime('2024-01-01-00:00:00', 'yyyy-MM-dd-HH:mm:ss') 
    AND 
    parse_datetime('2024-03-20-00:00:00', 'yyyy-MM-dd-HH:mm:ss') 
  ) 
  AND 
    (tls_handshake_latency >= 2.0);
```

# 其他资源
<a name="application-load-balancer-logs-additional-resources"></a>

有关使用 ALB 日志的更多信息，请参阅以下资源。
+ *AWS 知识中心*中的[如何使用 Amazon Athena 分析应用程序负载均衡器访问日志](https://repost.aws/knowledge-center/athena-analyze-access-logs)。
+ 有关弹性负载均衡 HTTP 状态代码的信息，请参阅《应用程序负载均衡器用户指南**》中的[对应用程序负载均衡器进行问题排查](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-troubleshooting.html)。
+ 在 *AWS 大数据博客*中的[使用 AWS Glue 自定义分类器和 Amazon Athena 更有效地编目和分析应用程序负载均衡器日志](https://aws.amazon.com/blogs/big-data/catalog-and-analyze-application-load-balancer-logs-more-efficiently-with-aws-glue-custom-classifiers-and-amazon-athena/)。

# 查询经典负载均衡器日志
<a name="elasticloadbalancer-classic-logs"></a>

使用经典负载均衡器日志，分析和了解传入和传出 Elastic Load Balancing 实例和后端应用程序的流量模式。您可以查看流量来源、延迟和已传输的字节。

在分析 Elastic Load Balancing 日志之前，请对其进行配置以保存在目标 Amazon S3 存储桶中。有关更多信息，请参阅[为经典负载均衡器启用访问日志](https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/enable-access-logs.html)。

**为 Elastic Load Balancing 日志创建表**

1. 将以下 DDL 语句复制并粘贴到 Athena 控制台中。检查 Elastic Load Balancing 日志的[语法](https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/access-log-collection.html#access-log-entry-format)。您可能需要更新以下查询以包含列和最新版本的记录的 Regex 语法。

   ```
   CREATE EXTERNAL TABLE IF NOT EXISTS elb_logs (
    
    timestamp string,
    elb_name string,
    request_ip string,
    request_port int,
    backend_ip string,
    backend_port int,
    request_processing_time double,
    backend_processing_time double,
    client_response_time double,
    elb_response_code string,
    backend_response_code string,
    received_bytes bigint,
    sent_bytes bigint,
    request_verb string,
    url string,
    protocol string,
    user_agent string,
    ssl_cipher string,
    ssl_protocol string
   )
   ROW FORMAT SERDE 'org.apache.hadoop.hive.serde2.RegexSerDe'
   WITH SERDEPROPERTIES (
    'serialization.format' = '1',
    'input.regex' = '([^ ]*) ([^ ]*) ([^ ]*):([0-9]*) ([^ ]*)[:-]([0-9]*) ([-.0-9]*) ([-.0-9]*) ([-.0-9]*) (|[-0-9]*) (-|[-0-9]*) ([-0-9]*) ([-0-9]*) \\\"([^ ]*) ([^ ]*) (- |[^ ]*)\\\" (\"[^\"]*\") ([A-Z0-9-]+) ([A-Za-z0-9.-]*)$'
   )
   LOCATION 's3://amzn-s3-demo-bucket/AWSLogs/AWS_account_ID/elasticloadbalancing/';
   ```

1. 修改 `LOCATION` Amazon S3 存储桶以指定您的 Elastic Load Balancing 日志的目标。

1. 在 Athena 控制台中运行查询。查询完成后，Athena 将注册 `elb_logs` 表，使其中的数据可以供查询。有关更多信息，请参阅 [示例查询](#query-elb-classic-example)。

## 示例查询
<a name="query-elb-classic-example"></a>

使用类似于以下示例的查询。它列出返回 `4XX` 或 `5XX` 错误响应代码的后端应用程序服务器。使用 `LIMIT` 运算符可限制每次查询的日志数目。

```
SELECT
 timestamp,
 elb_name,
 backend_ip,
 backend_response_code
FROM elb_logs
WHERE backend_response_code LIKE '4%' OR
      backend_response_code LIKE '5%'
LIMIT 100;
```

使用后续查询对按后端 IP 地址和 Elastic Load Balancing 实例名称分组的所有事务的响应时间进行求和。

```
SELECT sum(backend_processing_time) AS
 total_ms,
 elb_name,
 backend_ip
FROM elb_logs WHERE backend_ip <> ''
GROUP BY backend_ip, elb_name
LIMIT 100;
```

有关更多信息，请参阅[使用 Athena 分析 S3 中的数据](https://aws.amazon.com/blogs/big-data/analyzing-data-in-s3-using-amazon-athena/)。

# 查询 Amazon CloudFront 日志
<a name="cloudfront-logs"></a>

您可以配置 Amazon CloudFront CDN 以将 Web 分配访问日志导出到 Amazon Simple Storage Service。使用这些日志可跨 CloudFront 提供的 Web 属性浏览用户的网上冲浪模式。

在开始查询日志之前，请在您的首选 CloudFront 分配上启用 Web 分配访问登录。有关信息，请参阅《Amazon CloudFront 开发人员指南**》中的 [访问日志](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/AccessLogs.html)。记录保存这些日志的 Amazon S3 存储桶。

**Topics**
+ [为 CloudFront 标准日志创建表（旧版）](create-cloudfront-table-standard-logs.md)
+ [使用手动分区在 Athena 中为使用 JSON 的 CloudFront 日志创建表](create-cloudfront-table-manual-json.md)
+ [使用手动分区在 Athena 中为使用 Parquet 的 CloudFront 日志创建表](create-cloudfront-table-manual-parquet.md)
+ [使用分区投影在 Athena 中为使用 JSON 的 CloudFront 日志创建表](create-cloudfront-table-partition-json.md)
+ [使用分区投影在 Athena 中为使用 Parquet 的 CloudFront 日志创建表](create-cloudfront-table-partition-parquet.md)
+ [为 CloudFront 实时日志创建表](create-cloudfront-table-real-time-logs.md)
+ [其他资源](cloudfront-logs-additional-resources.md)

# 为 CloudFront 标准日志创建表（旧版）
<a name="create-cloudfront-table-standard-logs"></a>

**注意**  
以下过程适用于 CloudFront 中的 Web 分配访问日志。它不适用于 RTMP 分配中的流日志。

**为 CloudFront 标准日志文件字段创建表**

1. 将以下示例 DDL 语句复制并粘贴到 Athena 控制台的查询编辑器中。该示例语句使用《Amazon CloudFront 开发人员指南》**的[标准日志文件字段](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/AccessLogs.html#BasicDistributionFileFormat)部分中记录的日志文件字段。修改用于存储日志的 Amazon S3 存储桶的 `LOCATION`。有关使用查询编辑器的信息，请参阅 [开始使用](getting-started.md)。

   此查询指定了 `ROW FORMAT DELIMITED` 和 `FIELDS TERMINATED BY '\t'`，表示字段由制表符分隔。对于 `ROW FORMAT DELIMITED`，Athena 默认使用 [LazySimpleSerDe](lazy-simple-serde.md)。列 `date` 使用反引号 (`) 转义，因为它是 Athena 中的保留字。有关信息，请参阅[转义查询中的保留关键字](reserved-words.md)。

   ```
   CREATE EXTERNAL TABLE IF NOT EXISTS cloudfront_standard_logs (
     `date` DATE,
     time STRING,
     x_edge_location STRING,
     sc_bytes BIGINT,
     c_ip STRING,
     cs_method STRING,
     cs_host STRING,
     cs_uri_stem STRING,
     sc_status INT,
     cs_referrer STRING,
     cs_user_agent STRING,
     cs_uri_query STRING,
     cs_cookie STRING,
     x_edge_result_type STRING,
     x_edge_request_id STRING,
     x_host_header STRING,
     cs_protocol STRING,
     cs_bytes BIGINT,
     time_taken FLOAT,
     x_forwarded_for STRING,
     ssl_protocol STRING,
     ssl_cipher STRING,
     x_edge_response_result_type STRING,
     cs_protocol_version STRING,
     fle_status STRING,
     fle_encrypted_fields INT,
     c_port INT,
     time_to_first_byte FLOAT,
     x_edge_detailed_result_type STRING,
     sc_content_type STRING,
     sc_content_len BIGINT,
     sc_range_start BIGINT,
     sc_range_end BIGINT
   )
   ROW FORMAT DELIMITED 
   FIELDS TERMINATED BY '\t'
   LOCATION 's3://amzn-s3-demo-bucket/'
   TBLPROPERTIES ( 'skip.header.line.count'='2' )
   ```

1. 在 Athena 控制台中运行查询。查询完成后，Athena 将注册 `cloudfront_standard_logs` 表，使其中的数据可以供您发出查询。

## 示例查询
<a name="query-examples-cloudfront-logs"></a>

以下查询将累计 2018 年 6 月 9 日到 6 月 11 日之间由 CloudFront 提供的字节的数量。将日期列名称用双引号引起来，因为它是保留字。

```
SELECT SUM(bytes) AS total_bytes
FROM cloudfront_standard_logs
WHERE "date" BETWEEN DATE '2018-06-09' AND DATE '2018-06-11'
LIMIT 100;
```

要从查询结果中消除重复的行（例如，重复的空行），您可以使用 `SELECT DISTINCT` 语句，如以下示例所示。

```
SELECT DISTINCT * 
FROM cloudfront_standard_logs 
LIMIT 10;
```

# 使用手动分区在 Athena 中为使用 JSON 的 CloudFront 日志创建表
<a name="create-cloudfront-table-manual-json"></a>

**为使用 JSON 格式的 CloudFront 标准日志文件字段创建表**

1. 将以下示例 DDL 语句复制并粘贴到 Athena 控制台的查询编辑器中。该示例语句使用《Amazon CloudFront 开发人员指南》**的[标准日志文件字段](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/AccessLogs.html#BasicDistributionFileFormat)部分中记录的日志文件字段。修改用于存储日志的 Amazon S3 存储桶的 `LOCATION`。

   此查询使用 OpenX JSON SerDe 和以下 SerDe 属性来正确读取 Athena 中的 JSON 字段。

   ```
   CREATE EXTERNAL TABLE `cf_logs_manual_partition_json`(
     `date` string , 
     `time` string , 
     `x-edge-location` string , 
     `sc-bytes` string , 
     `c-ip` string , 
     `cs-method` string , 
     `cs(host)` string , 
     `cs-uri-stem` string , 
     `sc-status` string , 
     `cs(referer)` string , 
     `cs(user-agent)` string , 
     `cs-uri-query` string , 
     `cs(cookie)` string , 
     `x-edge-result-type` string , 
     `x-edge-request-id` string , 
     `x-host-header` string , 
     `cs-protocol` string , 
     `cs-bytes` string , 
     `time-taken` string , 
     `x-forwarded-for` string , 
     `ssl-protocol` string , 
     `ssl-cipher` string , 
     `x-edge-response-result-type` string , 
     `cs-protocol-version` string , 
     `fle-status` string , 
     `fle-encrypted-fields` string , 
     `c-port` string , 
     `time-to-first-byte` string , 
     `x-edge-detailed-result-type` string , 
     `sc-content-type` string , 
     `sc-content-len` string , 
     `sc-range-start` string , 
     `sc-range-end` string )
   ROW FORMAT SERDE 
     'org.openx.data.jsonserde.JsonSerDe' 
   WITH SERDEPROPERTIES ( 
     'paths'='c-ip,c-port,cs(Cookie),cs(Host),cs(Referer),cs(User-Agent),cs-bytes,cs-method,cs-protocol,cs-protocol-version,cs-uri-query,cs-uri-stem,date,fle-encrypted-fields,fle-status,sc-bytes,sc-content-len,sc-content-type,sc-range-end,sc-range-start,sc-status,ssl-cipher,ssl-protocol,time,time-taken,time-to-first-byte,x-edge-detailed-result-type,x-edge-location,x-edge-request-id,x-edge-response-result-type,x-edge-result-type,x-forwarded-for,x-host-header') 
   STORED AS INPUTFORMAT 
     'org.apache.hadoop.mapred.TextInputFormat' 
   OUTPUTFORMAT 
     'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'
   LOCATION
     's3://amzn-s3-demo-bucket/'
   ```

1. 在 Athena 控制台中运行查询。查询完成后，Athena 将注册 `cf_logs_manual_partition_json` 表，使其中的数据可以供您发出查询。

## 示例查询
<a name="query-examples-cloudfront-logs-manual-json"></a>

以下查询将累计 2025 年 1 月 15 日由 CloudFront 提供的字节数。

```
SELECT sum(cast("sc-bytes" as BIGINT)) as sc
FROM cf_logs_manual_partition_json
WHERE "date"='2025-01-15'
```

要从查询结果中消除重复的行（例如，重复的空行），您可以使用 `SELECT DISTINCT` 语句，如以下示例所示。

```
SELECT DISTINCT * FROM cf_logs_manual_partition_json
```

# 使用手动分区在 Athena 中为使用 Parquet 的 CloudFront 日志创建表
<a name="create-cloudfront-table-manual-parquet"></a>

**为使用 Parquet 格式的 CloudFront 标准日志文件字段创建表**

1. 将以下示例 DDL 语句复制并粘贴到 Athena 控制台的查询编辑器中。该示例语句使用《Amazon CloudFront 开发人员指南》**的[标准日志文件字段](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/AccessLogs.html#BasicDistributionFileFormat)部分中记录的日志文件字段。

   此查询使用 ParquetHiveSerDe 和以下 SerDe 属性来正确读取 Athena 中的 Parquet 字段。

   ```
   CREATE EXTERNAL TABLE `cf_logs_manual_partition_parquet`(
     `date` string, 
     `time` string, 
     `x_edge_location` string, 
     `sc_bytes` string, 
     `c_ip` string, 
     `cs_method` string, 
     `cs_host` string, 
     `cs_uri_stem` string, 
     `sc_status` string, 
     `cs_referer` string, 
     `cs_user_agent` string, 
     `cs_uri_query` string, 
     `cs_cookie` string, 
     `x_edge_result_type` string, 
     `x_edge_request_id` string, 
     `x_host_header` string, 
     `cs_protocol` string, 
     `cs_bytes` string, 
     `time_taken` string, 
     `x_forwarded_for` string, 
     `ssl_protocol` string, 
     `ssl_cipher` string, 
     `x_edge_response_result_type` string, 
     `cs_protocol_version` string, 
     `fle_status` string, 
     `fle_encrypted_fields` string, 
     `c_port` string, 
     `time_to_first_byte` string, 
     `x_edge_detailed_result_type` string, 
     `sc_content_type` string, 
     `sc_content_len` string, 
     `sc_range_start` string, 
     `sc_range_end` string)
   ROW FORMAT SERDE 
     'org.apache.hadoop.hive.ql.io.parquet.serde.ParquetHiveSerDe' 
   STORED AS INPUTFORMAT 
     'org.apache.hadoop.hive.ql.io.parquet.MapredParquetInputFormat' 
   OUTPUTFORMAT 
     'org.apache.hadoop.hive.ql.io.parquet.MapredParquetOutputFormat'
   LOCATION
     's3://amzn-s3-demo-bucket/'
   ```

1. 在 Athena 控制台中运行查询。查询完成后，Athena 将注册 `cf_logs_manual_partition_parquet` 表，使其中的数据可以供您发出查询。

## 示例查询
<a name="query-examples-cloudfront-logs-manual-parquet"></a>

以下查询将累计 2025 年 1 月 19 日由 CloudFront 提供的字节数。

```
SELECT sum(cast("sc_bytes" as BIGINT)) as sc
FROM cf_logs_manual_partition_parquet
WHERE "date"='2025-01-19'
```

要从查询结果中消除重复的行（例如，重复的空行），您可以使用 `SELECT DISTINCT` 语句，如以下示例所示。

```
SELECT DISTINCT * FROM cf_logs_manual_partition_parquet
```

# 使用分区投影在 Athena 中为使用 JSON 的 CloudFront 日志创建表
<a name="create-cloudfront-table-partition-json"></a>

您可以使用 Athena 分区投影功能缩短查询运行时间并自动化管理分区。当添加新数据时，分区投影会自动添加新分区。这样就不必使用 `ALTER TABLE ADD PARTITION` 手动添加分区了。

以下示例 CREATE TABLE 语句将为单个 AWS 区域中截至当前时间的，来自指定 CloudFront 分配的 CloudFront 日志，自动使用分区投影功能。成功运行查询后，您可以查询表。

```
CREATE EXTERNAL TABLE `cloudfront_logs_pp`(
  `date` string, 
  `time` string, 
  `x-edge-location` string, 
  `sc-bytes` string, 
  `c-ip` string, 
  `cs-method` string, 
  `cs(host)` string, 
  `cs-uri-stem` string, 
  `sc-status` string, 
  `cs(referer)` string, 
  `cs(user-agent)` string, 
  `cs-uri-query` string, 
  `cs(cookie)` string, 
  `x-edge-result-type` string, 
  `x-edge-request-id` string, 
  `x-host-header` string, 
  `cs-protocol` string, 
  `cs-bytes` string, 
  `time-taken` string, 
  `x-forwarded-for` string, 
  `ssl-protocol` string, 
  `ssl-cipher` string, 
  `x-edge-response-result-type` string, 
  `cs-protocol-version` string, 
  `fle-status` string, 
  `fle-encrypted-fields` string, 
  `c-port` string, 
  `time-to-first-byte` string, 
  `x-edge-detailed-result-type` string, 
  `sc-content-type` string, 
  `sc-content-len` string, 
  `sc-range-start` string, 
  `sc-range-end` string)
  PARTITIONED BY(
         distributionid string,
         year int,
         month int,
         day int,
         hour int )
ROW FORMAT SERDE 
  'org.openx.data.jsonserde.JsonSerDe'
WITH SERDEPROPERTIES ( 
  'paths'='c-ip,c-port,cs(Cookie),cs(Host),cs(Referer),cs(User-Agent),cs-bytes,cs-method,cs-protocol,cs-protocol-version,cs-uri-query,cs-uri-stem,date,fle-encrypted-fields,fle-status,sc-bytes,sc-content-len,sc-content-type,sc-range-end,sc-range-start,sc-status,ssl-cipher,ssl-protocol,time,time-taken,time-to-first-byte,x-edge-detailed-result-type,x-edge-location,x-edge-request-id,x-edge-response-result-type,x-edge-result-type,x-forwarded-for,x-host-header') 
STORED AS INPUTFORMAT 
  'org.apache.hadoop.mapred.TextInputFormat' 
OUTPUTFORMAT 
  'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'
LOCATION
  's3://amzn-s3-demo-bucket/AWSLogs/AWS_ACCOUNT_ID/CloudFront/'
TBLPROPERTIES (
  'projection.distributionid.type'='enum',
  'projection.distributionid.values'='E2Oxxxxxxxxxxx',
  'projection.day.range'='01,31', 
  'projection.day.type'='integer', 
  'projection.day.digits'='2', 
  'projection.enabled'='true', 
  'projection.month.range'='01,12', 
  'projection.month.type'='integer', 
  'projection.month.digits'='2', 
  'projection.year.range'='2025,2026', 
  'projection.year.type'='integer', 
  'projection.hour.range'='00,23',
  'projection.hour.type'='integer',
  'projection.hour.digits'='2',
  'storage.location.template'='s3://amzn-s3-demo-bucket/AWSLogs/AWS_ACCOUNT_ID/CloudFront/${distributionid}/${year}/${month}/${day}/${hour}/')
```

对于上一示例中使用的属性，应注意以下事项。
+ **表名** – 表名 *`cloudfront_logs_pp`* 是可替换的。您可以将其更改为自己喜欢的任何名称。
+ **位置** – 修改 `s3://amzn-s3-demo-bucket/AWSLogs/AWS_ACCOUNT_ID/` 以指向您的 Amazon S3 存储桶。
+ **分配 ID** – 对于 `projection.distributionid.values`，您可以通过用逗号分隔符来指定多个分配 ID。例如，*<distributionID1>*, *<distributionID2>*。
+ **年份范围**–在 `projection.year.range` 中，您可以根据自己的数据设置年份范围。您可以将其调整为任何期间，例如 *2025*、*2026*。
**注意**  
如果包含空分区，例如针对未来日期的分区（例如：2025-2040），则可能会影响查询性能。但是，分区投影旨在有效处理未来日期。为保持最佳性能，请务必要谨慎管理分区，尽可能避免过多空分区。
+ **存储位置模板** – 您必须确保根据以下 CloudFront 分区结构和 S3 路径正确更新 `storage.location.template`。  
****    
[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/athena/latest/ug/create-cloudfront-table-partition-json.html)

  在确认 CloudFront 分区结构和 S3 结构符合所需模式后，请按如下方式更新 `storage.location.template`：

  ```
  'storage.location.template'='s3://amzn-s3-demo-bucket/AWSLogs/account_id/CloudFront/${distributionid}/folder2/${year}/${month}/${day}/${hour}/folder3/'
  ```
**注意**  
正确配置 `storage.location.template` 对于确保正确的数据存储和检索至关重要。

# 使用分区投影在 Athena 中为使用 Parquet 的 CloudFront 日志创建表
<a name="create-cloudfront-table-partition-parquet"></a>

以下示例 CREATE TABLE 语句将为单个 AWS 区域中截至当前时间的，来自指定 CloudFront 分配的 Parquet 格式的 CloudFront 日志，自动使用分区投影功能。成功运行查询后，您可以查询表。

```
CREATE EXTERNAL TABLE `cloudfront_logs_parquet_pp`(
`date` string, 
`time` string, 
`x_edge_location` string, 
`sc_bytes` string, 
`c_ip` string, 
`cs_method` string, 
`cs_host` string, 
`cs_uri_stem` string, 
`sc_status` string, 
`cs_referer` string, 
`cs_user_agent` string, 
`cs_uri_query` string, 
`cs_cookie` string, 
`x_edge_result_type` string, 
`x_edge_request_id` string, 
`x_host_header` string, 
`cs_protocol` string, 
`cs_bytes` string, 
`time_taken` string, 
`x_forwarded_for` string, 
`ssl_protocol` string, 
`ssl_cipher` string, 
`x_edge_response_result_type` string, 
`cs_protocol_version` string, 
`fle_status` string, 
`fle_encrypted_fields` string, 
`c_port` string, 
`time_to_first_byte` string, 
`x_edge_detailed_result_type` string, 
`sc_content_type` string, 
`sc_content_len` string, 
`sc_range_start` string, 
`sc_range_end` string)
PARTITIONED BY(
 distributionid string,
 year int,
 month int,
 day int,
 hour int )
ROW FORMAT SERDE 
'org.apache.hadoop.hive.ql.io.parquet.serde.ParquetHiveSerDe' 
STORED AS INPUTFORMAT 
'org.apache.hadoop.hive.ql.io.parquet.MapredParquetInputFormat' 
OUTPUTFORMAT 
'org.apache.hadoop.hive.ql.io.parquet.MapredParquetOutputFormat'
LOCATION
's3://amzn-s3-demo-bucket/AWSLogs/AWS_ACCOUNT_ID/CloudFront/'
TBLPROPERTIES (
'projection.distributionid.type'='enum',
'projection.distributionid.values'='E3OK0LPUNWWO3',
'projection.day.range'='01,31',
'projection.day.type'='integer',
'projection.day.digits'='2',
'projection.enabled'='true',
'projection.month.range'='01,12',
'projection.month.type'='integer',
'projection.month.digits'='2',
'projection.year.range'='2019,2025',
'projection.year.type'='integer',
'projection.hour.range'='01,12',
'projection.hour.type'='integer',
'projection.hour.digits'='2',
'storage.location.template'='s3://amzn-s3-demo-bucket/AWSLogs/AWS_ACCOUNT_ID/CloudFront/${distributionid}/${year}/${month}/${day}/${hour}/')
```

对于上一示例中使用的属性，应注意以下事项。
+ **表名** – 表名 *`cloudfront_logs_pp`* 是可替换的。您可以将其更改为自己喜欢的任何名称。
+ **位置** – 修改 `s3://amzn-s3-demo-bucket/AWSLogs/AWS_ACCOUNT_ID/` 以指向您的 Amazon S3 存储桶。
+ **分配 ID** – 对于 `projection.distributionid.values`，您可以通过用逗号分隔符来指定多个分配 ID。例如，*<distributionID1>*, *<distributionID2>*。
+ **年份范围**–在 `projection.year.range` 中，您可以根据自己的数据设置年份范围。您可以将其调整为任何期间，例如 *2025*、*2026*。
**注意**  
如果包含空分区，例如针对未来日期的分区（例如：2025-2040），则可能会影响查询性能。但是，分区投影旨在有效处理未来日期。为保持最佳性能，请务必要谨慎管理分区，尽可能避免过多空分区。
+ **存储位置模板** – 您必须确保根据以下 CloudFront 分区结构和 S3 路径正确更新 `storage.location.template`。  
****    
[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/athena/latest/ug/create-cloudfront-table-partition-parquet.html)

  在确认 CloudFront 分区结构和 S3 结构符合所需模式后，请按如下方式更新 `storage.location.template`：

  ```
  'storage.location.template'='s3://amzn-s3-demo-bucket/AWSLogs/account_id/CloudFront/${distributionid}/folder2/${year}/${month}/${day}/${hour}/folder3/'
  ```
**注意**  
正确配置 `storage.location.template` 对于确保正确的数据存储和检索至关重要。

# 为 CloudFront 实时日志创建表
<a name="create-cloudfront-table-real-time-logs"></a>

**为 CloudFront 实时日志文件字段创建表**

1. 将以下示例 DDL 语句复制并粘贴到 Athena 控制台的查询编辑器中。该示例语句使用了《Amazon CloudFront Developer Guide》**的 [Real-time logs](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/real-time-logs.html) 章节中记录的日志文件字段。修改用于存储日志的 Amazon S3 存储桶的 `LOCATION`。有关使用查询编辑器的信息，请参阅 [开始使用](getting-started.md)。

   此查询指定了 `ROW FORMAT DELIMITED` 和 `FIELDS TERMINATED BY '\t'`，表示字段由制表符分隔。对于 `ROW FORMAT DELIMITED`，Athena 默认使用 [LazySimpleSerDe](lazy-simple-serde.md)。列 `timestamp` 使用反引号 (`) 转义，因为它是 Athena 中的保留字。有关信息，请参阅[转义查询中的保留关键字](reserved-words.md)。

   以下示例包含所有可用字段。若有不需要的字段，可将相应字段注释掉或者删除掉。

   ```
   CREATE EXTERNAL TABLE IF NOT EXISTS cloudfront_real_time_logs ( 
   `timestamp` STRING,
   c_ip STRING,
   time_to_first_byte BIGINT,
   sc_status BIGINT,
   sc_bytes BIGINT,
   cs_method STRING,
   cs_protocol STRING,
   cs_host STRING,
   cs_uri_stem STRING,
   cs_bytes BIGINT,
   x_edge_location STRING,
   x_edge_request_id STRING,
   x_host_header STRING,
   time_taken BIGINT,
   cs_protocol_version STRING,
   c_ip_version STRING,
   cs_user_agent STRING,
   cs_referer STRING,
   cs_cookie STRING,
   cs_uri_query STRING,
   x_edge_response_result_type STRING,
   x_forwarded_for STRING,
   ssl_protocol STRING,
   ssl_cipher STRING,
   x_edge_result_type STRING,
   fle_encrypted_fields STRING,
   fle_status STRING,
   sc_content_type STRING,
   sc_content_len BIGINT,
   sc_range_start STRING,
   sc_range_end STRING,
   c_port BIGINT,
   x_edge_detailed_result_type STRING,
   c_country STRING,
   cs_accept_encoding STRING,
   cs_accept STRING,
   cache_behavior_path_pattern STRING,
   cs_headers STRING,
   cs_header_names STRING,
   cs_headers_count BIGINT,
   primary_distribution_id STRING,
   primary_distribution_dns_name STRING,
   origin_fbl STRING,
   origin_lbl STRING,
   asn STRING
   )
   ROW FORMAT DELIMITED 
   FIELDS TERMINATED BY '\t'
   LOCATION 's3://amzn-s3-demo-bucket/'
   TBLPROPERTIES ( 'skip.header.line.count'='2' )
   ```

1. 在 Athena 控制台中运行查询。查询完成后，Athena 将注册 `cloudfront_real_time_logs` 表，使其中的数据可以供您发出查询。

# 其他资源
<a name="cloudfront-logs-additional-resources"></a>

有关使用 Athena 查询 CloudFront 日志的详细信息，请参阅 [AWS 大数据博客](https://aws.amazon.com/blogs/big-data/)中的以下文章。

[使用 Amazon Athena 轻松查询 AWS 服务 日志](https://aws.amazon.com/blogs/big-data/easily-query-aws-service-logs-using-amazon-athena/)（2019 年 5 月 29 日）。

[大规模分析 Amazon CloudFront 访问日志](https://aws.amazon.com/blogs/big-data/analyze-your-amazon-cloudfront-access-logs-at-scale/)（2018 年 12 月 21 日）。

[使用 AWS Lambda、Amazon Athena 和适用于 Apache Flink 的亚马逊托管服务构建无服务器架构来分析 Amazon CloudFront 访问日志](https://aws.amazon.com/blogs/big-data/build-a-serverless-architecture-to-analyze-amazon-cloudfront-access-logs-using-aws-lambda-amazon-athena-and-amazon-kinesis-analytics/)（2017 年 5 月 26 日）。

# 查询 AWS CloudTrail日志
<a name="cloudtrail-logs"></a>

AWS CloudTrail 是一项服务，可记录 AWS API 调用和 Amazon Web Services 账户的事件。

CloudTrail 日志包含有关对 AWS 服务 进行的任何 API 调用的详细信息，包括控制台。CloudTrail 生成加密的日志文件并将其存储在 Amazon S3 中。有关更多信息，请参阅《[AWS CloudTrail 用户指南](https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-user-guide.html)》。

**注意**  
如果您需要跨账户、区域和日期对 CloudTrail 事件信息执行 SQL 查询，请考虑使用 CloudTrail Lake。CloudTrail Lake 是一种可用于创建跟踪的替代 AWS 服务，可将来自企业的信息聚合到单个可搜索的事件数据存储中。它不使用 Amazon S3 存储桶存储，而是将事件存储在某个数据湖中，从而支持更丰富、更快的查询。您可以使用它创建 SQL 查询，以便在自定义时间范围内跨组织和区域搜索事件。由于 CloudTrail Lake 查询是在 CloudTrail 控制台中执行，因此使用 CloudTrail Lake 不需要 Athena。有关更多信息，请参阅 [CloudTrail Lake](https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-lake.html) 文档。

将 Athena 与 CloudTrail 日志结合使用是加强对 AWS 服务 活动进行分析的强有力方法。例如，您可以使用查询来确定趋势，并根据属性 (如源 IP 地址或用户) 进一步隔离活动。

常见的应用程序是使用 CloudTrail 日志分析运营活动的安全性和合规性。有关详细示例的信息，请参阅 AWS 大数据博客文章：[使用 AWS CloudTrail 和 Amazon Athena 分析安全性、合规性和运营活动](https://aws.amazon.com/blogs/big-data/aws-cloudtrail-and-amazon-athena-dive-deep-to-analyze-security-compliance-and-operational-activity/)。

您可以使用 Athena 通过指定日志文件的 `LOCATION` 直接从 Amazon S3 查询这些日志文件。您可以通过两种方式之一来执行此操作：
+ 通过直接从 CloudTrail 控制台为 CloudTrail 日志文件创建表。
+ 通过在 Athena 控制台中手动为 CloudTrail 日志文件创建表。

**Topics**
+ [了解 CloudTrail 日志和 Athena 表](create-cloudtrail-table-understanding.md)
+ [使用 CloudTrail 控制台为 CloudTrail 日志创建 Athena 表](create-cloudtrail-table-ct.md)
+ [使用手动分区在 Athena 中为 CloudTrail 日志创建表](create-cloudtrail-table.md)
+ [使用手动分区为整个组织范围的跟踪创建表](create-cloudtrail-table-org-wide-trail.md)
+ [使用分区投影在 Athena 中为 CloudTrail 日志创建表](create-cloudtrail-table-partition-projection.md)
+ [CloudTrail 日志查询示例](query-examples-cloudtrail-logs.md)

# 了解 CloudTrail 日志和 Athena 表
<a name="create-cloudtrail-table-understanding"></a>

在开始创建表之前，应了解有关 CloudTrail 以及它如何存储数据的更多信息。这有助于创建所需的表，无论您从 CloudTrail 控制台或从 Athena 创建它们都是如此。

CloudTrail 将日志另存为采用压缩 gzip 格式的 JSON 文本文件（`*.json.gz`）。日志文件的位置取决于您如何设置跟踪、您正在记录的一个 AWS 区域 或多个区域以及其他因素。

有关日志存储位置、JSON 结构和记录文件内容的更多信息，请参阅《[AWS CloudTrail 用户指南](https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-user-guide.html)》中的以下主题：
+  [查找 CloudTrail 日志文件](https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-find-log-files.html) 
+  [CloudTrail 日志文件示例](https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-log-file-examples.html) 
+  [CloudTrail 记录内容](https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-event-reference-record-contents.html)
+  [CloudTrail 事件参考](https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-event-reference.html) 

要收集日志并将其保存到 Amazon S3，请从 AWS 管理控制台 启用 CloudTrail。有关更多信息，请参阅《AWS CloudTrail 用户指南**》中的 [创建跟踪记录](https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-create-a-trail-using-the-console-first-time.html)。

# 使用 CloudTrail 控制台为 CloudTrail 日志创建 Athena 表
<a name="create-cloudtrail-table-ct"></a>

您可以创建未分区的 Athena 表，来直接从较旧的 CloudTrail 控制台查询 CloudTrail 日志。若要从 CloudTrail 控制台创建 Athena 表，您需要使用具有在 Athena 中创建表的足够权限的角色登录。

**注意**  
您不能使用 CloudTrail 控制台为组织跟踪记录日志创建 Athena 表。相反，请使用 Athena 控制台手动创建表，以便指定正确的存储位置。有关组织跟踪的信息，请参阅《*AWS CloudTrail 用户指南*》中的[为组织创建跟踪记录](https://docs.aws.amazon.com/awscloudtrail/latest/userguide/creating-trail-organization.html)。
+ 有关设置 Athena 权限的信息，请参阅 [设置、管理和编程访问](setting-up.md)。
+ 有关创建带分区的表的信息，请参阅[使用手动分区在 Athena 中为 CloudTrail 日志创建表](create-cloudtrail-table.md)。

**使用 CloudTrail 控制台为 CloudTrail 跟踪记录创建 Athena 表**

1. 访问 [https://console.aws.amazon.com/cloudtrail/](https://console.aws.amazon.com/cloudtrail/)，打开 CloudTrail 控制台。

1. 在导航窗格中，选择**事件历史记录**。

1. 选择 **Create Athena table**（创建 Athena 表）。  
![\[选择 Create Athena table（创建 Athena 表）\]](http://docs.aws.amazon.com/zh_cn/athena/latest/ug/images/cloudtrail-logs-create-athena-table.png)

1. 对于 **Storage location**（存储位置），使用向下箭头选择其中存储了供跟踪查询的日志文件的 Amazon S3 存储桶。
**注意**  
要查找与跟踪记录关联的存储桶的名称，请选择 CloudTrail 导航窗格中的 **Trails**（跟踪记录），然后查看跟踪记录的 **S3 bucket**（S3 存储桶）列。要查看 Amazon S3 中存储桶的位置，请在 **S3 bucket**（S3 存储桶）列中选择存储桶的链接。这样，就可以将 Amazon S3 控制台打开到 CloudTrail 存储桶位置。

1. 选择**创建表**。将使用包含 Amazon S3 存储桶名称的默认名称创建表。

# 使用手动分区在 Athena 中为 CloudTrail 日志创建表
<a name="create-cloudtrail-table"></a>

可以在 Athena 控制台中为 CloudTrail 日志文件手动创建表，然后在 Athena 中运行查询。

**使用 Athena 控制台为 CloudTrail 跟踪记录创建 Athena 表**

1. 将以下 DDL 语句复制并粘贴到 Athena 控制台查询编辑器中，然后根据要求进行修改。请注意，由于 CloudTrail 日志文件不是公用 API 调用的有序堆栈跟踪，因此日志文件中的字段不会按照任何特定顺序显示。

   ```
   CREATE EXTERNAL TABLE cloudtrail_logs (
   eventversion STRING,
   useridentity STRUCT<
                  type:STRING,
                  principalid:STRING,
                  arn:STRING,
                  accountid:STRING,
                  invokedby:STRING,
                  accesskeyid:STRING,
                  username:STRING,
                  onbehalfof: STRUCT<
                       userid: STRING,
                       identitystorearn: STRING>,
     sessioncontext:STRUCT<
       attributes:STRUCT<
                  mfaauthenticated:STRING,
                  creationdate:STRING>,
       sessionissuer:STRUCT<  
                  type:STRING,
                  principalid:STRING,
                  arn:STRING, 
                  accountid:STRING,
                  username:STRING>,
       ec2roledelivery:string,
       webidfederationdata: STRUCT<
                  federatedprovider: STRING,
                  attributes: map<string,string>>
     >
   >,
   eventtime STRING,
   eventsource STRING,
   eventname STRING,
   awsregion STRING,
   sourceipaddress STRING,
   useragent STRING,
   errorcode STRING,
   errormessage STRING,
   requestparameters STRING,
   responseelements STRING,
   additionaleventdata STRING,
   requestid STRING,
   eventid STRING,
   resources ARRAY<STRUCT<
                  arn:STRING,
                  accountid:STRING,
                  type:STRING>>,
   eventtype STRING,
   apiversion STRING,
   readonly STRING,
   recipientaccountid STRING,
   serviceeventdetails STRING,
   sharedeventid STRING,
   vpcendpointid STRING,
   vpcendpointaccountid STRING,
   eventcategory STRING,
   addendum STRUCT<
     reason:STRING,
     updatedfields:STRING,
     originalrequestid:STRING,
     originaleventid:STRING>,
   sessioncredentialfromconsole STRING,
   edgedevicedetails STRING,
   tlsdetails STRUCT<
     tlsversion:STRING,
     ciphersuite:STRING,
     clientprovidedhostheader:STRING>
   )
   PARTITIONED BY (region string, year string, month string, day string)
   ROW FORMAT SERDE 'org.apache.hive.hcatalog.data.JsonSerDe'
   STORED AS INPUTFORMAT 'com.amazon.emr.cloudtrail.CloudTrailInputFormat'
   OUTPUTFORMAT 'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'
   LOCATION 's3://amzn-s3-demo-bucket/AWSLogs/Account_ID/';
   ```
**注意**  
建议使用示例中所示的 `org.apache.hive.hcatalog.data.JsonSerDe`。虽然存在 `com.amazon.emr.hive.serde.CloudTrailSerde`，但目前还无法处理一些较新的 CloudTrail 字段。

1. （可选）删除表格中所有非必填字段。如果您只需要读取一组特定的列，则您的表定义可以排除其他列。

1. 修改 `s3://amzn-s3-demo-bucket/AWSLogs/Account_ID/` 以指向包含要查询的日志数据的 Amazon S3 存储桶。该示例使用特定账户的日志的 `LOCATION` 值，但您可以使用最适合您的应用程序的明确度程度。例如：
   + 要分析多个账户中的数据，您可以通过使用 `LOCATION 's3://amzn-s3-demo-bucket/AWSLogs/'`，回滚 `LOCATION` 说明符来指示所有 `AWSLogs`。
   + 要分析特定日期、账户和区域中的数据，请使用 `LOCATION 's3://amzn-s3-demo-bucket/123456789012/CloudTrail/us-east-1/2016/03/14/'.` 
   + 要分析网络活动数据而不是管理事件，请将 `LOCATION` 子句中的 `/CloudTrail/` 替换为 `/CloudTrail-NetworkActivity/`。

   如果使用对象层次结构中的最高级别，则可以在使用 Athena 查询时获得最高程度的灵活度。

1. 验证字段是否正确列出。有关 CloudTrail 记录中的完整字段列表的更多信息，请参阅 [CloudTrail 记录内容](https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-event-reference-record-contents.html)。

   步骤 1 中的示例 `CREATE TABLE` 语句使用 [Hive JSON SerDe](hive-json-serde.md)。在该示例中，字段 `requestparameters`、`responseelements` 和 `additionaleventdata` 作为查询中的 `STRING` 类型列出，但在 JSON 中使用 `STRUCT` 数据类型。因此，要将数据移出这些字段，请使用 `JSON_EXTRACT` 函数。有关更多信息，请参阅 [从字符串中提取 JSON 数据](extracting-data-from-JSON.md)。为了提高性能，此示例按 AWS 区域、年份、月份和日期对数据进行分区。

1. 在 Athena 控制台中运行 `CREATE TABLE` 语句。

1. 使用 [ALTER TABLE ADD PARTITION](alter-table-add-partition.md) 命令加载分区，以便您可以查询它们，如以下示例所示。

   ```
   ALTER TABLE table_name ADD 
      PARTITION (region='us-east-1',
                 year='2019',
                 month='02',
                 day='01')
      LOCATION 's3://amzn-s3-demo-bucket/AWSLogs/Account_ID/CloudTrail/us-east-1/2019/02/01/'
   ```

# 使用手动分区为整个组织范围的跟踪创建表
<a name="create-cloudtrail-table-org-wide-trail"></a>

要在 Athena 中为整个组织的 CloudTrail 日志文件创建表，请按照 [使用手动分区在 Athena 中为 CloudTrail 日志创建表](create-cloudtrail-table.md) 中的步骤操作，但需要按照以下过程中的说明进行修改。

**为整个组织的 CloudTrail 日志记录创建 Athena 表**

1. 在 `CREATE TABLE` 语句中，修改 `LOCATION` 子句以包含组织 ID，如下例所示：

   ```
   LOCATION 's3://amzn-s3-demo-bucket/AWSLogs/organization_id/'
   ```

1. 在 `PARTITIONED BY` 子句中，为账户 ID 添加一个字符串条目，如下例所示：

   ```
   PARTITIONED BY (account string, region string, year string, month string, day string)
   ```

   以下示例显示的是综合结果：

   ```
   ...
   
   PARTITIONED BY (account string, region string, year string, month string, day string) 
   ROW FORMAT SERDE 'org.apache.hive.hcatalog.data.JsonSerDe'
   STORED AS INPUTFORMAT 'com.amazon.emr.cloudtrail.CloudTrailInputFormat'
   OUTPUTFORMAT 'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'
   LOCATION 's3://amzn-s3-demo-bucket/AWSLogs/organization_id/Account_ID/CloudTrail/'
   ```

1. `ALTER TABLE` 语句的 `ADD PARTITION` 子句包含账户 ID，如下例所示：

   ```
   ALTER TABLE table_name ADD
   PARTITION (account='111122223333',
   region='us-east-1',
   year='2022',
   month='08',
   day='08')
   ```

1. `ALTER TABLE` 语句的 `LOCATION` 子句包含组织 ID、账户 ID 以及您要添加的分区，如下例所示：

   ```
   LOCATION 's3://amzn-s3-demo-bucket/AWSLogs/organization_id/Account_ID/CloudTrail/us-east-1/2022/08/08/'
   ```

   以下示例 `ALTER TABLE` 语句显示的是综合结果：

   ```
   ALTER TABLE table_name ADD
   PARTITION (account='111122223333',
   region='us-east-1',
   year='2022',
   month='08',
   day='08')
   LOCATION 's3://amzn-s3-demo-bucket/AWSLogs/organization_id/111122223333/CloudTrail/us-east-1/2022/08/08/'
   ```

请注意，在大型组织中，使用此方法为每个组织帐户 ID 手动添加和维护分区可能很麻烦。在这种情况下，请考虑使用 CloudTrail Lake 而不是 Athena。在这种情况下，CloudTrail Lake 具有以下优势：
+ 自动聚合整个组织的日志
+ 不需要设置或维护分区或 Athena 表
+ 查询直接在 CloudTrail 控制台中运行
+ 使用与 SQL 兼容的查询语言

有关更多信息，请参阅《*AWS CloudTrail 用户指南*》中的[使用 AWS CloudTrail Lake](https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-lake.html)。

# 使用分区投影在 Athena 中为 CloudTrail 日志创建表
<a name="create-cloudtrail-table-partition-projection"></a>

由于 CloudTrail 日志具有一个已知结构，您可以预先指定该结构的分区方案，因此可以使用 Athena 分区投影功能减少查询运行时间并自动管理分区。当添加新数据时，分区投影会自动添加新分区。这样就不必使用 `ALTER TABLE ADD PARTITION` 手动添加分区了。

以下示例 `CREATE TABLE` 语句会自动在 CloudTrail 日志上从指定日期开始到当前为单个 AWS 区域 使用分区投影。在 `LOCATION` 和 `storage.location.template` 子句中，将*存储桶*、*account-id* 和 *aws-region* 占位符替换为对应的相同值。对于 `projection.timestamp.range`，将 *2020*/*01*/*01* 替换为要使用的开始日期。成功运行查询后，您可以查询表。您无需运行 `ALTER TABLE ADD PARTITION` 来加载分区。

```
CREATE EXTERNAL TABLE cloudtrail_logs_pp(
    eventversion STRING,
    useridentity STRUCT<
        type: STRING,
        principalid: STRING,
        arn: STRING,
        accountid: STRING,
        invokedby: STRING,
        accesskeyid: STRING,
        username: STRING,
        onbehalfof: STRUCT<
             userid: STRING,
             identitystorearn: STRING>,
        sessioncontext: STRUCT<
            attributes: STRUCT<
                mfaauthenticated: STRING,
                creationdate: STRING>,
            sessionissuer: STRUCT<
                type: STRING,
                principalid: STRING,
                arn: STRING,
                accountid: STRING,
                username: STRING>,
            ec2roledelivery:string,
            webidfederationdata: STRUCT<
                federatedprovider: STRING,
                attributes: map<string,string>>
        >
    >,
    eventtime STRING,
    eventsource STRING,
    eventname STRING,
    awsregion STRING,
    sourceipaddress STRING,
    useragent STRING,
    errorcode STRING,
    errormessage STRING,
    requestparameters STRING,
    responseelements STRING,
    additionaleventdata STRING,
    requestid STRING,
    eventid STRING,
    readonly STRING,
    resources ARRAY<STRUCT<
        arn: STRING,
        accountid: STRING,
        type: STRING>>,
    eventtype STRING,
    apiversion STRING,
    recipientaccountid STRING,
    serviceeventdetails STRING,
    sharedeventid STRING,
    vpcendpointid STRING,
    vpcendpointaccountid STRING,
    eventcategory STRING,
    addendum STRUCT<
      reason:STRING,
      updatedfields:STRING,
      originalrequestid:STRING,
      originaleventid:STRING>,
    sessioncredentialfromconsole STRING,
    edgedevicedetails STRING,
    tlsdetails STRUCT<
      tlsversion:STRING,
      ciphersuite:STRING,
      clientprovidedhostheader:STRING>
  )
PARTITIONED BY (
   `timestamp` string)
ROW FORMAT SERDE 'org.apache.hive.hcatalog.data.JsonSerDe'
STORED AS INPUTFORMAT 'com.amazon.emr.cloudtrail.CloudTrailInputFormat'
OUTPUTFORMAT 'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'
LOCATION
  's3://amzn-s3-demo-bucket/AWSLogs/account-id/CloudTrail/aws-region'
TBLPROPERTIES (
  'projection.enabled'='true', 
  'projection.timestamp.format'='yyyy/MM/dd', 
  'projection.timestamp.interval'='1', 
  'projection.timestamp.interval.unit'='DAYS', 
  'projection.timestamp.range'='2020/01/01,NOW', 
  'projection.timestamp.type'='date', 
  'storage.location.template'='s3://amzn-s3-demo-bucket/AWSLogs/account-id/CloudTrail/aws-region/${timestamp}')
```

更多有关分区投影的信息，请参阅 [将分区投影与 Amazon Athena 结合使用](partition-projection.md)。

# CloudTrail 日志查询示例
<a name="query-examples-cloudtrail-logs"></a>

以下示例显示从在 CloudTrail 事件日志创建的表，返回所有匿名（未签名）请求的查询部分。此查询选择 `useridentity.accountid` 匿名并且 `useridentity.arn` 未指定的那些请求：

```
SELECT *
FROM cloudtrail_logs
WHERE 
    eventsource = 's3.amazonaws.com' AND 
    eventname in ('GetObject') AND 
    useridentity.accountid = 'anonymous' AND 
    useridentity.arn IS NULL AND
    requestparameters LIKE '%[your bucket name ]%';
```

有关更多信息，请参阅 AWS 大数据博客文章：[使用 AWS CloudTrail 和 Amazon Athena 分析安全性、合规性和运营活动](https://aws.amazon.com/blogs/big-data/aws-cloudtrail-and-amazon-athena-dive-deep-to-analyze-security-compliance-and-operational-activity/)。

## 查询 CloudTrail 日志中的嵌套字段
<a name="cloudtrail-logs-nested-fields"></a>

由于 `userIdentity` 和 `resources` 字段是嵌套的数据类型，查询这些内容需要特殊处理。

`userIdentity` 对象由嵌套 `STRUCT` 类型组成。可以使用点分隔字段以分隔待查询的字段，如下例所示：

```
SELECT 
    eventsource, 
    eventname,
    useridentity.sessioncontext.attributes.creationdate,
    useridentity.sessioncontext.sessionissuer.arn
FROM cloudtrail_logs
WHERE useridentity.sessioncontext.sessionissuer.arn IS NOT NULL
ORDER BY eventsource, eventname
LIMIT 10
```

`resources` 字段是一个 `STRUCT` 对象数组。对于这些数组，请使用 `CROSS JOIN UNNEST` 来取消嵌套数组，以便您可以查询其对象。

下面的示例将返回资源 ARN 以 `example/datafile.txt` 结尾的所有行。为了便于读取，[replace](https://prestodb.io/docs/current/functions/string.html#replace) 函数将从 ARN 中删除初始 `arn:aws:s3:::` 子字符串。

```
SELECT 
    awsregion,
    replace(unnested.resources_entry.ARN,'arn:aws:s3:::') as s3_resource,
    eventname,
    eventtime,
    useragent
FROM cloudtrail_logs t
CROSS JOIN UNNEST(t.resources) unnested (resources_entry)
WHERE unnested.resources_entry.ARN LIKE '%example/datafile.txt'
ORDER BY eventtime
```

以下是 `DeleteBucket` 事件的示例查询。查询将从 `resources` 对象中提取存储桶的名称以及存储桶所属的账户 ID。

```
SELECT 
    awsregion,
    replace(unnested.resources_entry.ARN,'arn:aws:s3:::') as deleted_bucket,
    eventtime AS time_deleted,
    useridentity.username, 
    unnested.resources_entry.accountid as bucket_acct_id 
FROM cloudtrail_logs t
CROSS JOIN UNNEST(t.resources) unnested (resources_entry)
WHERE eventname = 'DeleteBucket'
ORDER BY eventtime
```

有关取消嵌套的更多信息，请参阅 [筛选数组](filtering-arrays.md)。

## 有关查询 CloudTrail 日志的提示
<a name="tips-for-querying-cloudtrail-logs"></a>

在探索 CloudTrail 日志数据时考虑以下各项：
+ 在查询日志之前，请验证您的日志表是否看似与[使用手动分区在 Athena 中为 CloudTrail 日志创建表](create-cloudtrail-table.md)中的表一样。如果不是第一个表，请使用以下命令删除现有表：`DROP TABLE cloudtrail_logs`。
+ 删除现有表后，重新创建它。有关更多信息，请参阅 [使用手动分区在 Athena 中为 CloudTrail 日志创建表](create-cloudtrail-table.md)。

  确认正确列出了 Athena 查询中的字段。有关 CloudTrail 记录中的完整字段列表的信息，请参阅 [CloudTrail 记录内容](https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-event-reference-record-contents.html)。

  如果您的查询包含 JSON 格式的字段，例如 `STRUCT`，请从 JSON 中提取数据。有关更多信息，请参阅 [从字符串中提取 JSON 数据](extracting-data-from-JSON.md)。

  关于针对 CloudTrail 表发布查询的一些建议如下：
+ 首先查看哪些用户调用了哪些 API 操作以及来自哪些源 IP 地址。
+ 将以下基本 SQL 查询用作您的模板。将查询粘贴到 Athena 控制台并运行它。

  ```
  SELECT
   useridentity.arn,
   eventname,
   sourceipaddress,
   eventtime
  FROM cloudtrail_logs
  LIMIT 100;
  ```
+ 修改查询以进一步探索您的数据。
+ 为了提高性能，请包含 `LIMIT` 子句以返回指定的行子集。

# 查询 Amazon EMR 日志
<a name="emr-logs"></a>

Amazon EMR 和在 Amazon EMR 上运行的大数据应用程序会生成日志文件。日志文件写入[主节点](https://docs.aws.amazon.com/emr/latest/ManagementGuide/emr-master-core-task-nodes.html)，您还可以配置 Amazon EMR 将日志文件自动归档到 Amazon S3。您可以使用 Amazon Athena 查询这些日志，以确定应用程序和集群的事件和趋势。有关 Amazon EMR 中日志文件类型以及将其保存到 Simple Storage Service (Amazon S3) 的更多信息，请参阅《Amazon EMR 管理指南**》中的[查看日志文件](https://docs.aws.amazon.com/emr/latest/ManagementGuide/emr-manage-view-web-log-files.html)。

**Topics**
+ [基于 Amazon EMR 日志文件创建和查询基本表](emr-create-table.md)
+ [基于 Amazon EMR 日志创建和查询分区表](emr-create-table-partitioned.md)

# 基于 Amazon EMR 日志文件创建和查询基本表
<a name="emr-create-table"></a>

以下示例基于保存到的 `s3://aws-logs-123456789012-us-west-2/elasticmapreduce/j-2ABCDE34F5GH6/elasticmapreduce/` 日志文件创建基本表 `myemrlogs`。以下示例中使用的 Amazon S3 位置反映了 Amazon Web Services 账户 *123456789012* 在区域 *us-west-2* 中创建的 EMR 集群的默认日志位置的模式。如果使用自定义位置，则模式为 s3://amzn-s3-demo-bucket/*ClusterID*。

有关创建分区表以潜在改善查询性能和减少数据传输的信息，请参阅 [基于 Amazon EMR 日志创建和查询分区表](emr-create-table-partitioned.md)。

```
CREATE EXTERNAL TABLE `myemrlogs`(
  `data` string COMMENT 'from deserializer')
ROW FORMAT DELIMITED  
FIELDS TERMINATED BY '|'
LINES TERMINATED BY '\n'
STORED AS INPUTFORMAT 
  'org.apache.hadoop.mapred.TextInputFormat' 
OUTPUTFORMAT 
  'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'
LOCATION
  's3://aws-logs-123456789012-us-west-2/elasticmapreduce/j-2ABCDE34F5GH6'
```

## 示例查询
<a name="emr-example-queries-basic"></a>

以下示例查询可以在上一示例创建的 `myemrlogs` 表上运行。

**Example – 查询步骤日志以查找出现的 ERROR、WARN、INFO、EXCEPTION、FATAL 或 DEBUG**  

```
SELECT data,
        "$PATH"
FROM "default"."myemrlogs"
WHERE regexp_like("$PATH",'s-86URH188Z6B1')
        AND regexp_like(data, 'ERROR|WARN|INFO|EXCEPTION|FATAL|DEBUG') limit 100;
```

**Example – 查询特定实例日志 i-00b3c0a839ece0a9c 以查找 ERROR、WARN、INFO、EXCEPTION、FATAL 或 DEBUG**  

```
SELECT "data",
        "$PATH" AS filepath
FROM "default"."myemrlogs"
WHERE regexp_like("$PATH",'i-00b3c0a839ece0a9c')
        AND regexp_like("$PATH",'state')
        AND regexp_like(data, 'ERROR|WARN|INFO|EXCEPTION|FATAL|DEBUG') limit 100;
```

**Example – 查询 Presto 应用程序日志以查找 ERROR、WARN、INFO、EXCEPTION、FATAL 或 DEBUG**  

```
SELECT "data",
        "$PATH" AS filepath
FROM "default"."myemrlogs"
WHERE regexp_like("$PATH",'presto')
        AND regexp_like(data, 'ERROR|WARN|INFO|EXCEPTION|FATAL|DEBUG') limit 100;
```

**Example – 查询 Namenode 应用程序日志以查找 ERROR、WARN、INFO、EXCEPTION、FATAL 或 DEBUG**  

```
SELECT "data",
        "$PATH" AS filepath
FROM "default"."myemrlogs"
WHERE regexp_like("$PATH",'namenode')
        AND regexp_like(data, 'ERROR|WARN|INFO|EXCEPTION|FATAL|DEBUG') limit 100;
```

**Example – 按日期和小时查询所有日志以查找 ERROR、WARN、INFO、EXCEPTION、FATAL 或 DEBUG**  

```
SELECT distinct("$PATH") AS filepath
FROM "default"."myemrlogs"
WHERE regexp_like("$PATH",'2019-07-23-10')
        AND regexp_like(data, 'ERROR|WARN|INFO|EXCEPTION|FATAL|DEBUG') limit 100;
```

# 基于 Amazon EMR 日志创建和查询分区表
<a name="emr-create-table-partitioned"></a>

这些示例使用相同的日志位置创建 Athena 表，但对表进行了分区，然后为每个日志位置创建一个分区。有关更多信息，请参阅 [对您的数据进行分区](partitions.md)。

以下查询将创建名为 `mypartitionedemrlogs` 的分区表：

```
CREATE EXTERNAL TABLE `mypartitionedemrlogs`(
  `data` string COMMENT 'from deserializer')
 partitioned by (logtype string)
ROW FORMAT DELIMITED  
FIELDS TERMINATED BY '|'
LINES TERMINATED BY '\n'
STORED AS INPUTFORMAT 
  'org.apache.hadoop.mapred.TextInputFormat' 
OUTPUTFORMAT 
 'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'
LOCATION 's3://aws-logs-123456789012-us-west-2/elasticmapreduce/j-2ABCDE34F5GH6'
```

然后，以下查询语句基于 Amazon EMR 在 Amazon S3: 中创建的不同日志类型的子目录创建表分区：

```
ALTER TABLE mypartitionedemrlogs ADD
     PARTITION (logtype='containers')
     LOCATION 's3://aws-logs-123456789012-us-west-2/elasticmapreduce/j-2ABCDE34F5GH6/containers/'
```

```
ALTER TABLE mypartitionedemrlogs ADD
     PARTITION (logtype='hadoop-mapreduce')
     LOCATION 's3://aws-logs-123456789012-us-west-2/elasticmapreduce/j-2ABCDE34F5GH6/hadoop-mapreduce/'
```

```
ALTER TABLE mypartitionedemrlogs ADD
     PARTITION (logtype='hadoop-state-pusher')
     LOCATION 's3://aws-logs-123456789012-us-west-2/elasticmapreduce/j-2ABCDE34F5GH6/hadoop-state-pusher/'
```

```
ALTER TABLE mypartitionedemrlogs ADD
     PARTITION (logtype='node')
     LOCATION 's3://aws-logs-123456789012-us-west-2/elasticmapreduce/j-2ABCDE34F5GH6/node/'
```

```
ALTER TABLE mypartitionedemrlogs ADD
     PARTITION (logtype='steps')
     LOCATION 's3://aws-logs-123456789012-us-west-2/elasticmapreduce/j-2ABCDE34F5GH6/steps/'
```

创建分区后，您可以在表上运行 `SHOW PARTITIONS` 查询以确认：

```
SHOW PARTITIONS mypartitionedemrlogs;
```

## 示例查询
<a name="emr-example-queries-partitioned"></a>

以下示例演示对特定日志条目的查询使用由上述示例创建的表和分区。

**Example – 查询容器分区中的应用程序 application\$11561661818238\$10002 日志以查找 ERROR 或 WARN**  

```
SELECT data,
        "$PATH"
FROM "default"."mypartitionedemrlogs"
WHERE logtype='containers'
        AND regexp_like("$PATH",'application_1561661818238_0002')
        AND regexp_like(data, 'ERROR|WARN') limit 100;
```

**Example – 查询 hadoop-Mapreduce 分区以查找任务 job\$11561661818238\$10004 和失败的减少操作**  

```
SELECT data,
        "$PATH"
FROM "default"."mypartitionedemrlogs"
WHERE logtype='hadoop-mapreduce'
        AND regexp_like(data,'job_1561661818238_0004|Failed Reduces') limit 100;
```

**Example – 查询节点分区中的 Hive 日志以查找查询 ID 056e0609-33e1-4611-956c-7a31b42d2663**  

```
SELECT data,
        "$PATH"
FROM "default"."mypartitionedemrlogs"
WHERE logtype='node'
        AND regexp_like("$PATH",'hive')
        AND regexp_like(data,'056e0609-33e1-4611-956c-7a31b42d2663') limit 100;
```

**Example – 查询节点分区中的 resourcemanager 日志以查找应用程序 1567660019320\$10001\$101\$1000001**  

```
SELECT data,
        "$PATH"
FROM "default"."mypartitionedemrlogs"
WHERE logtype='node'
        AND regexp_like(data,'resourcemanager')
        AND regexp_like(data,'1567660019320_0001_01_000001') limit 100
```

# 查询 AWS Global Accelerator 流日志
<a name="querying-global-accelerator-flow-logs"></a>

可以使用 AWS Global Accelerator 创建加速器，从而将网络流量定向到 AWS 全局网络上的最佳终端节点。有关 Global Accelerator 的更多信息，请参阅[什么是 AWS Global Accelerator](https://docs.aws.amazon.com/global-accelerator/latest/dg/what-is-global-accelerator.html)。

Global Accelerator 流日志允许您捕获有关进出加速器中的网络接口的 IP 地址流量的信息。流日志数据将发布到 Amazon S3，您可以在其中检索和查看您的数据。有关更多信息，请参阅 [AWS Global Accelerator 中的流日志](https://docs.aws.amazon.com/global-accelerator/latest/dg/monitoring-global-accelerator.flow-logs.html)。

可以使用 Athena 查询 Global Accelerator 流日志，方式是创建一个指定流日志在 Amazon S3 中的位置的表。

**为 Global Accelerator 流日志创建表**

1. 将以下 DDL 语句复制并粘贴到 Athena 控制台中。此查询指定 *ROW FORMAT DELIMITED* 并且不再指定 [SerDe](serde-reference.md)，这意味着查询将使用 [`LazySimpleSerDe`](lazy-simple-serde.md)。在此查询中，字段由一个空格终止。

   ```
   CREATE EXTERNAL TABLE IF NOT EXISTS aga_flow_logs (
     version string,
     account string,
     acceleratorid string,
     clientip string,
     clientport int,
     gip string,
     gipport int,
     endpointip string,
     endpointport int,
     protocol string,
     ipaddresstype string,
     numpackets bigint,
     numbytes int,
     starttime int,
     endtime int,
     action string,
     logstatus string,
     agasourceip string,
     agasourceport int,
     endpointregion string,
     agaregion string,
     direction string,
     vpc_id string,
     reject_reason string
   )
   PARTITIONED BY (dt string)
   ROW FORMAT DELIMITED
   FIELDS TERMINATED BY ' '
   LOCATION 's3://amzn-s3-demo-bucket/prefix/AWSLogs/account_id/globalaccelerator/region/'
   TBLPROPERTIES ("skip.header.line.count"="1");
   ```

1. 修改 `LOCATION` 值以指向包含您的日志数据的 Amazon S3 存储桶。

   ```
   's3://amzn-s3-demo-bucket/prefix/AWSLogs/account_id/globalaccelerator/region_code/'
   ```

1. 在 Athena 控制台中运行查询。查询完成后，Athena 注册 `aga_flow_logs` 表，使其中的数据可用于查询。

1. 创建分区可读取数据，如以下示例查询中所示。此查询创建指定日期的单个分区。替换日期和位置的占位符。

   ```
   ALTER TABLE aga_flow_logs
   ADD PARTITION (dt='YYYY-MM-dd')
   LOCATION 's3://amzn-s3-demo-bucket/prefix/AWSLogs/account_id/globalaccelerator/region_code/YYYY/MM/dd';
   ```

## AWS Global Accelerator 流日志的示例查询
<a name="querying-global-accelerator-flow-logs-examples"></a>

**Example – 列出通过特定边缘站点的请求**  
以下示例查询列出了已通过 LHR 边缘站点的请求。使用 `LIMIT` 运算符可限制一次查询的日志数目。  

```
SELECT 
  clientip, 
  agaregion, 
  protocol, 
  action 
FROM 
  aga_flow_logs 
WHERE 
  agaregion LIKE 'LHR%' 
LIMIT 
  100;
```

**Example – 列出接收大多数 HTTPS 请求的端点 IP 地址**  
要查看哪些终端节点 IP 地址收到的 HTTPS 请求最多，请使用以下查询。此查询计算在 HTTPS 端口 443 上接收的数据包数，按目标 IP 地址对它们进行分组，并返回前 10 个 IP 地址。  

```
SELECT 
  SUM(numpackets) AS packetcount, 
  endpointip 
FROM 
  aga_flow_logs 
WHERE 
  endpointport = 443 
GROUP BY 
  endpointip 
ORDER BY 
  packetcount DESC 
LIMIT 
  10;
```

# 查询 Amazon GuardDuty 调查发现
<a name="querying-guardduty"></a>

[Amazon GuardDuty](https://aws.amazon.com/guardduty/) 是一项安全监控服务，用于帮助识别您 AWS 环境中的意外活动和潜在的未经授权或恶意活动。当检测到意外和潜在恶意活动时，GuardDuty 会生成安全[调查结果](https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_findings.html)，您可以将其导出到 Amazon S3 进行存储和分析。将调查结果导出到 Amazon S3 后，您可以使用 Athena 进行查询。本文介绍如何在 Athena 中为 GuardDuty 调查结果创建表并查询它们。

有关 Amazon GuardDuty 更多信息，请参阅《[Amazon GuardDuty 用户指南](https://docs.aws.amazon.com/guardduty/latest/ug/)》。

## 先决条件
<a name="querying-guardduty-prerequisites"></a>
+ 启用 GuardDuty 功能以将调查结果导出到 Amazon S3。有关步骤，请参阅《Amazon GuardDuty 用户指南》中[导出调查结果](https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_exportfindings.html)。

## 在 Athena 中为 GuardDuty 调查发现创建表
<a name="querying-guardduty-creating-a-table-in-athena-for-guardduty-findings"></a>

要从 Athena 查询您的 GuardDuty 调查结果，您必须为它们创建一个表。

**要在 Athena 中为 GuardDuty 调查结果创建表**

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

1. 将以下 DDL 语句粘贴到 Athena 控制台中。修改 `LOCATION 's3://amzn-s3-demo-bucket/AWSLogs/account-id/GuardDuty/'` 中的值以指向您在 Amazon S3 中的 GuardDuty 调查结果。

   ```
   CREATE EXTERNAL TABLE `gd_logs` (
     `schemaversion` string,
     `accountid` string,
     `region` string,
     `partition` string,
     `id` string,
     `arn` string,
     `type` string,
     `resource` string,
     `service` string,
     `severity` string,
     `createdat` string,
     `updatedat` string,
     `title` string,
     `description` string)
   ROW FORMAT SERDE 'org.openx.data.jsonserde.JsonSerDe'
    LOCATION 's3://amzn-s3-demo-bucket/AWSLogs/account-id/GuardDuty/'
    TBLPROPERTIES ('has_encrypted_data'='true')
   ```
**注意**  
SerDe 期望每个 JSON 文档都位于单行文本中，并且不使用行终止字符分隔记录中的字段。如果 JSON 文本采用美观的打印格式，当您在创建表后尝试对其进行查询时，可能会收到类似以下内容的错误消息：HIVE\$1CURSOR\$1ERROR: Row is not a valid JSON Object（HIVE\$1CURSOR\$1ERROR：行不是有效的 JSON 对象）或 HIVE\$1CURSOR\$1ERROR: JsonParseException: Unexpected end-of-input: expected close marker for OBJECT（HIVE\$1CURSOR\$1ERROR：JsonParseException：意外的输入结束：对象的预期关闭标记）。有关更多信息，请参阅 GitHub 上 OpenX SerDe 文档中的 [JSON 数据文件](https://github.com/rcongiu/Hive-JSON-Serde#json-data-files)。

1. 在 Athena 控制台中运行查询以注册 `gd_logs` 表。查询完成后，调查结果准备就绪，可供您从 Athena 查询。

## 示例查询
<a name="querying-guardduty-examples"></a>

以下示例演示了如何从 Athena 查询 GuardDuty 调查结果。

**Example – DNS 数据泄露**  
以下查询返回可能会通过 DNS 查询泄露数据的 Amazon EC2 实例的相关信息。  

```
SELECT
    title,
    severity,
    type,
    id AS FindingID,
    accountid,
    region,
    createdat,
    updatedat,
    json_extract_scalar(service, '$.count') AS Count,
    json_extract_scalar(resource, '$.instancedetails.instanceid') AS InstanceID,
    json_extract_scalar(service, '$.action.actiontype') AS DNS_ActionType,
    json_extract_scalar(service, '$.action.dnsrequestaction.domain') AS DomainName,
    json_extract_scalar(service, '$.action.dnsrequestaction.protocol') AS protocol,
    json_extract_scalar(service, '$.action.dnsrequestaction.blocked') AS blocked
FROM gd_logs
WHERE type = 'Trojan:EC2/DNSDataExfiltration'
ORDER BY severity DESC
```

**Example – 未授权的 IAM 用户访问**  
以下查询返回所有区域中 IAM 委托人的所有 `UnauthorizedAccess:IAMUser` 调查结果类型。  

```
SELECT title,
         severity,
         type,
         id,
         accountid,
         region,
         createdat,
         updatedat,
         json_extract_scalar(service, '$.count') AS Count, 
         json_extract_scalar(resource, '$.accesskeydetails.username') AS IAMPrincipal, 
         json_extract_scalar(service,'$.action.awsapicallaction.api') AS APIActionCalled
FROM gd_logs
WHERE type LIKE '%UnauthorizedAccess:IAMUser%' 
ORDER BY severity desc;
```

## 有关查询 GuardDuty 调查结果的提示
<a name="querying-guardduty-tips"></a>

创建查询时，请记住以下几点。
+ 要从嵌套 JSON 字段中提取数据，请使用 Presto `json_extract` 或 `json_extract_scalar` 函数。有关更多信息，请参阅 [从字符串中提取 JSON 数据](extracting-data-from-JSON.md)。
+ 请确保 JSON 字段中的所有字符均为小写字符。
+  有关下载查询结果的信息，请参阅[使用 Athena 控制台下载查询结果文件](saving-query-results.md)。

# 查询 AWS Network Firewall日志
<a name="querying-network-firewall-logs"></a>

AWS Network Firewall 是一种托管式服务，您可以使用它为您的 Amazon Virtual Private Cloud 实例部署必要的网络保护。AWS Network Firewall 与 AWS Firewall Manager 结合使用，以便您可以基于 AWS Network Firewall 规则构建策略，然后在您的 VPC 和账户中集中应用这些策略。有关 AWS Network Firewall 的更多信息，请参阅 [AWS Network Firewall](https://aws.amazon.com/network-firewall/)。

您可以为您转发到防火墙有状态规则引擎的流量配置 AWS Network Firewall 日志记录。日志记录为您提供有关网络流量的详细信息，包括有状态引擎接收数据包的时间、有关数据包的详细信息以及针对数据包采取的任何有状态规则操作。日志将发布到您配置的日志目标，您可以在其中检索和查看日志。有关更多信息，请参阅《*AWS Network Firewall 开发人员指南*》中的[录入来自 AWS Network Firewall 的网络流量](https://docs.aws.amazon.com/network-firewall/latest/developerguide/firewall-logging.html)。

**Topics**
+ [为警报日志创建和查询表](querying-network-firewall-logs-sample-alert-logs-table.md)
+ [为 netflow 日志创建和查询表](querying-network-firewall-logs-sample-netflow-logs-table.md)

# 为警报日志创建和查询表
<a name="querying-network-firewall-logs-sample-alert-logs-table"></a>

1. 修改下面的 DDL 语句示例，使其符合警报日志的结构。您可能需要更新语句以包含最新版本日志的列。有关更多信息，请参阅《*AWS Network Firewall 开发人员指南*》中的[防火墙日志的内容](https://docs.aws.amazon.com/network-firewall/latest/developerguide/firewall-logging.html#firewall-logging-contents)。

   ```
   CREATE EXTERNAL TABLE network_firewall_alert_logs (
     firewall_name string,
     availability_zone string,
     event_timestamp string,
     event struct<
       timestamp:string,
       flow_id:bigint,
       event_type:string,
       src_ip:string,
       src_port:int,
       dest_ip:string,
       dest_port:int,
       proto:string,
       app_proto:string,
       sni:string,
       tls_inspected:boolean,
       tls_error:struct<
         error_message:string>,
       revocation_check:struct<
         leaf_cert_fpr:string,
         status:string,
         action:string>,
       alert:struct<
         alert_id:string,
         alert_type:string,
         action:string,
         signature_id:int,
         rev:int,
         signature:string,
         category:string,
         severity:int,
         rule_name:string,
         alert_name:string,
         alert_severity:string,
         alert_description:string,
         file_name:string,
         file_hash:string,
         packet_capture:string,
         reference_links:array<string>
       >, 
       src_country:string, 
       dest_country:string, 
       src_hostname:string, 
       dest_hostname:string, 
       user_agent:string, 
       url:string
      >
   )
    ROW FORMAT SERDE 'org.openx.data.jsonserde.JsonSerDe'
    LOCATION 's3://amzn-s3-demo-bucket/path_to_alert_logs_folder/';
   ```

1. 修改 `LOCATION` 子句以指定您在 Amazon S3 中的日志文件夹。

1. 在 Athena 查询编辑器中运行 `CREATE TABLE` 查询。查询完成后，Athena 将注册 `network_firewall_alert_logs` 表，使其指向的数据可以进行查询。

## 示例查询
<a name="querying-network-firewall-logs-alert-log-sample-query"></a>

本节中的示例警报日志查询会筛选执行了 TLS 检查且警报严重性级别为 2 或更高的事件。

查询使用别名来创建输出列标题，以显示列所属的 `struct`。例如，`event.alert.category` 字段的列标题是 `event_alert_category`，而不只是 `category`。若要进一步自定义列名，可以根据自己的喜好修改别名。例如，可以使用下划线或其他分隔符来分隔 `struct` 名称和字段名称。

请记得根据表定义和查询结果中所需的字段修改列名和 `struct` 引用。

```
SELECT 
  firewall_name,
  availability_zone,
  event_timestamp,
  event.timestamp AS event_timestamp,
  event.flow_id AS event_flow_id,
  event.event_type AS event_type,
  event.src_ip AS event_src_ip,
  event.src_port AS event_src_port,
  event.dest_ip AS event_dest_ip,
  event.dest_port AS event_dest_port,
  event.proto AS event_protol,
  event.app_proto AS event_app_proto,
  event.sni AS event_sni,
  event.tls_inspected AS event_tls_inspected,
  event.tls_error.error_message AS event_tls_error_message,
  event.revocation_check.leaf_cert_fpr AS event_revocation_leaf_cert,
  event.revocation_check.status AS event_revocation_check_status,
  event.revocation_check.action AS event_revocation_check_action,
  event.alert.alert_id AS event_alert_alert_id,
  event.alert.alert_type AS event_alert_alert_type,
  event.alert.action AS event_alert_action,
  event.alert.signature_id AS event_alert_signature_id,
  event.alert.rev AS event_alert_rev,
  event.alert.signature AS event_alert_signature,
  event.alert.category AS event_alert_category,
  event.alert.severity AS event_alert_severity,
  event.alert.rule_name AS event_alert_rule_name,
  event.alert.alert_name AS event_alert_alert_name,
  event.alert.alert_severity AS event_alert_alert_severity,
  event.alert.alert_description AS event_alert_alert_description,
  event.alert.file_name AS event_alert_file_name,
  event.alert.file_hash AS event_alert_file_hash,
  event.alert.packet_capture AS event_alert_packet_capture,
  event.alert.reference_links AS event_alert_reference_links,
  event.src_country AS event_src_country,
  event.dest_country AS event_dest_country,
  event.src_hostname AS event_src_hostname,
  event.dest_hostname AS event_dest_hostname,
  event.user_agent AS event_user_agent,
  event.url AS event_url
FROM 
  network_firewall_alert_logs 
WHERE 
  event.alert.severity >= 2
  AND event.tls_inspected = true 
LIMIT 10;
```

# 为 netflow 日志创建和查询表
<a name="querying-network-firewall-logs-sample-netflow-logs-table"></a>

1. 修改下面的 DDL 语句示例，使其符合 netflow 日志的结构。您可能需要更新语句以包含最新版本日志的列。有关更多信息，请参阅《*AWS Network Firewall 开发人员指南*》中的[防火墙日志的内容](https://docs.aws.amazon.com/network-firewall/latest/developerguide/firewall-logging.html#firewall-logging-contents)。

   ```
   CREATE EXTERNAL TABLE network_firewall_netflow_logs (
     firewall_name string,
     availability_zone string,
     event_timestamp string,
     event struct<
       timestamp:string,
       flow_id:bigint,
       event_type:string,
       src_ip:string,
       src_port:int,
       dest_ip:string,
       dest_port:int,
       proto:string,
       app_proto:string,
       tls_inspected:boolean,
       netflow:struct<
         pkts:int,
         bytes:bigint,
         start:string,
         `end`:string,
         age:int,
         min_ttl:int,
         max_ttl:int,
         tcp_flags:struct<
           syn:boolean,
           fin:boolean,
           rst:boolean,
           psh:boolean,
           ack:boolean,
           urg:boolean
           >
         >
       >
   )
   ROW FORMAT SERDE 'org.openx.data.jsonserde.JsonSerDe' 
   LOCATION 's3://amzn-s3-demo-bucket/path_to_netflow_logs_folder/';
   ```

1. 修改 `LOCATION` 子句以指定您在 Amazon S3 中的日志文件夹。

1. 在 Athena 查询编辑器中运行 `CREATE TABLE` 查询。查询完成后，Athena 将注册 `network_firewall_netflow_logs` 表，使其指向的数据可以进行查询。

## 示例查询
<a name="querying-network-firewall-logs-netflow-log-sample-query"></a>

本节中的 netflow 日志查询示例会筛选执行了 TLS 检查的事件。

查询使用别名来创建输出列标题，以显示列所属的 `struct`。例如，`event.netflow.bytes` 字段的列标题是 `event_netflow_bytes`，而不只是 `bytes`。若要进一步自定义列名，可以根据自己的喜好修改别名。例如，可以使用下划线或其他分隔符来分隔 `struct` 名称和字段名称。

请记得根据表定义和查询结果中所需的字段修改列名和 `struct` 引用。

```
SELECT
  event.src_ip AS event_src_ip,
  event.dest_ip AS event_dest_ip,
  event.proto AS event_proto,
  event.app_proto AS event_app_proto,
  event.tls_inspected AS event_tls_inspected,
  event.netflow.pkts AS event_netflow_pkts,
  event.netflow.bytes AS event_netflow_bytes,
  event.netflow.tcp_flags.syn AS event_netflow_tcp_flags_syn 
FROM network_firewall_netflow_logs 
WHERE event.tls_inspected = true
```

# 查询网络负载均衡器日志
<a name="networkloadbalancer-classic-logs"></a>

使用 Athena 分析和处理来自 Network Load Balancer 的日志。这些日志将接收有关发送到 Network Load Balancer 的传输层安全性 (TLS) 请求的详细信息。您可以使用这些访问日志分析流量模式并解决问题。

在分析 Network Load Balancer 访问日志之前，请启用并配置它们以保存在目标 Amazon S3 存储桶中。有关更多信息以及有关每个网络负载均衡器访问日志条目的信息，请参阅[网络负载均衡器的访问日志](https://docs.aws.amazon.com/elasticloadbalancing/latest/network/load-balancer-access-logs.html)。

**为 Network Load Balancer 日志创建表**

1. 将以下 DDL 语句复制并粘贴到 Athena 控制台中。检查 Network Load Balancer 日志记录的[语法](https://docs.aws.amazon.com/elasticloadbalancing/latest/network/load-balancer-access-logs.html#access-log-file-format)。根据需要更新语句，以包含与日志记录对应的列和正则表达式。

   ```
   CREATE EXTERNAL TABLE IF NOT EXISTS nlb_tls_logs (
               type string,
               version string,
               time string,
               elb string,
               listener_id string,
               client_ip string,
               client_port int,
               target_ip string,
               target_port int,
               tcp_connection_time_ms double,
               tls_handshake_time_ms double,
               received_bytes bigint,
               sent_bytes bigint,
               incoming_tls_alert int,
               cert_arn string,
               certificate_serial string,
               tls_cipher_suite string,
               tls_protocol_version string,
               tls_named_group string,
               domain_name string,
               alpn_fe_protocol string,
               alpn_be_protocol string,
               alpn_client_preference_list string,
               tls_connection_creation_time string
               )
               ROW FORMAT SERDE 'org.apache.hadoop.hive.serde2.RegexSerDe'
               WITH SERDEPROPERTIES (
               'serialization.format' = '1',
               'input.regex' = 
               '([^ ]*) ([^ ]*) ([^ ]*) ([^ ]*) ([^ ]*) ([^ ]*):([0-9]*) ([^ ]*):([0-9]*) ([-.0-9]*) ([-.0-9]*) ([-0-9]*) ([-0-9]*) ([-0-9]*) ([^ ]*) ([^ ]*) ([^ ]*) ([^ ]*) ([^ ]*) ([^ ]*) ([^ ]*) ([^ ]*) ([^ ]*) ?([^ ]*)?( .*)?'
               )
               LOCATION 's3://amzn-s3-demo-bucket/AWSLogs/AWS_account_ID/elasticloadbalancing/region';
   ```

1. 修改 `LOCATION` Amazon S3 存储桶以指定您的 Network Load Balancer 日志的目标。

1. 在 Athena 控制台中运行查询。查询完成后，Athena 将注册 `nlb_tls_logs` 表，使其中的数据可以供查询。

## 示例查询
<a name="query-nlb-example"></a>

要查看证书的使用次数，请使用与以下示例类似的查询：

```
SELECT count(*) AS 
         ct,
         cert_arn
FROM "nlb_tls_logs"
GROUP BY  cert_arn;
```

以下查询显示了所用 TLS 版本低于 1.3 的用户数：

```
SELECT tls_protocol_version,
         COUNT(tls_protocol_version) AS 
         num_connections,
         client_ip
FROM "nlb_tls_logs"
WHERE tls_protocol_version < 'tlsv13'
GROUP BY tls_protocol_version, client_ip;
```

使用以下查询可确定需要较长的 TLS 握手时间的连接：

```
SELECT *
FROM "nlb_tls_logs"
ORDER BY  tls_handshake_time_ms DESC 
LIMIT 10;
```

使用以下查询可识别和统计过去 30 天内协商的 TLS 协议版本和密码套件。

```
SELECT tls_cipher_suite,
         tls_protocol_version,
         COUNT(*) AS ct
FROM "nlb_tls_logs"
WHERE from_iso8601_timestamp(time) > current_timestamp - interval '30' day
        AND NOT tls_protocol_version = '-'
GROUP BY tls_cipher_suite, tls_protocol_version
ORDER BY ct DESC;
```

# 查询 Amazon Route 53 Resolver 查询日志
<a name="querying-r53-resolver-logs"></a>

您可以为 Amazon Route 53 Resolver 查询日志创建 Athena 表，并从 Athena 查询这些日志。

Route 53 解析程序查询日志记录用于记录 VPC 中的资源提出的 DNS 查询、使用入站解析程序端点的本地部署资源、使用出站解析程序端点进行递归 DNS 解析的查询以及使用 Route 53 解析程序 DNS 防火墙规则阻止、允许或监控域列表的查询。有关解析程序查询日志记录的更多信息，请参阅《*Amazon Route 53 开发人员指南*》中的[解析程序查询日志记录](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/resolver-query-logs.html)。有关日志中每个字段的信息，请参阅《**Amazon Route 53 开发人员指南》中的 [显示在解析程序查询日志中的值](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/resolver-query-logs-format.html)。

**Topics**
+ [为解析程序查询日志创建表](querying-r53-resolver-logs-creating-the-table.md)
+ [使用分区投影](querying-r53-resolver-logs-partitioning-example.md)
+ [示例查询](querying-r53-resolver-logs-example-queries.md)

# 为解析程序查询日志创建表
<a name="querying-r53-resolver-logs-creating-the-table"></a>

您可以使用 Athena 控制台中的查询编辑器为 Route 53 解析程序查询日志创建和查询表。

**为 Route 53 解析程序查询日志创建和查询 Athena 表**

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

1. 在 Athena 查询编辑器中，输入以下 `CREATE TABLE` 语句。将 `LOCATION` 子句值替换为与您的解析程序日志在 Amazon S3 中的位置相对应的子句值。

   ```
   CREATE EXTERNAL TABLE r53_rlogs (
     version string,
     account_id string,
     region string,
     vpc_id string,
     query_timestamp string,
     query_name string,
     query_type string,
     query_class
       string,
     rcode string,
     answers array<
       struct<
         Rdata: string,
         Type: string,
         Class: string>
       >,
     srcaddr string,
     srcport int,
     transport string,
     srcids struct<
       instance: string,
       resolver_endpoint: string
       >,
     firewall_rule_action string,
     firewall_rule_group_id string,
     firewall_domain_list_id string
    )
        
   ROW FORMAT SERDE 'org.openx.data.jsonserde.JsonSerDe'
   LOCATION 's3://amzn-s3-demo-bucket/AWSLogs/aws_account_id/vpcdnsquerylogs/{vpc-id}/'
   ```

   由于解析程序查询日志数据是 JSON 格式的，因此 CREATE TABLE 语句将使用 [JSON SerDe 库](json-serde.md)来分析数据。
**注意**  
SerDe 期望每个 JSON 文档都位于单行文本中，并且不使用行终止字符分隔记录中的字段。如果 JSON 文本采用美观的打印格式，当您在创建表后尝试对其进行查询时，可能会收到类似以下内容的错误消息：HIVE\$1CURSOR\$1ERROR: Row is not a valid JSON Object（HIVE\$1CURSOR\$1ERROR：行不是有效的 JSON 对象）或 HIVE\$1CURSOR\$1ERROR: JsonParseException: Unexpected end-of-input: expected close marker for OBJECT（HIVE\$1CURSOR\$1ERROR：JsonParseException：意外的输入结束：对象的预期关闭标记）。有关更多信息，请参阅 GitHub 上 OpenX SerDe 文档中的 [JSON 数据文件](https://github.com/rcongiu/Hive-JSON-Serde#json-data-files)。

1. 选择**运行查询**。该语句创建名为 `r53_rlogs` 的 Athena 表，其中的列表示解析程序日志数据中的每个字段。

1. 在 Athena 控制台查询编辑器中，运行以下查询以验证您的表是否已创建。

   ```
   SELECT * FROM "r53_rlogs" LIMIT 10
   ```

# 使用分区投影
<a name="querying-r53-resolver-logs-partitioning-example"></a>

以下示例显示了 Resolver 查询日志的 `CREATE TABLE` 语句，该语句使用分区投影并按 VPC 和日期进行分区。更多有关分区投影的信息，请参阅 [将分区投影与 Amazon Athena 结合使用](partition-projection.md)。

```
CREATE EXTERNAL TABLE r53_rlogs (
  version string,
  account_id string,
  region string,
  vpc_id string,
  query_timestamp string,
  query_name string,
  query_type string,
  query_class string,
  rcode string,
  answers array<
    struct<
      Rdata: string,
      Type: string,
      Class: string>
    >,
  srcaddr string,
  srcport int,
  transport string,
  srcids struct<
    instance: string,
    resolver_endpoint: string
    >,
  firewall_rule_action string,
  firewall_rule_group_id string,
  firewall_domain_list_id string
)
PARTITIONED BY (
`date` string,
`vpc` string
)
ROW FORMAT SERDE      'org.openx.data.jsonserde.JsonSerDe'
STORED AS INPUTFORMAT 'org.apache.hadoop.mapred.TextInputFormat'
OUTPUTFORMAT          'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'
LOCATION              's3://amzn-s3-demo-bucket/route53-query-logging/AWSLogs/aws_account_id/vpcdnsquerylogs/'
TBLPROPERTIES(
'projection.enabled' = 'true',
'projection.vpc.type' = 'enum',
'projection.vpc.values' = 'vpc-6446ae02',
'projection.date.type' = 'date',
'projection.date.range' = '2023/06/26,NOW',
'projection.date.format' = 'yyyy/MM/dd',
'projection.date.interval' = '1',
'projection.date.interval.unit' = 'DAYS',
'storage.location.template' = 's3://amzn-s3-demo-bucket/route53-query-logging/AWSLogs/aws_account_id/vpcdnsquerylogs/${vpc}/${date}/'
)
```

# 示例查询
<a name="querying-r53-resolver-logs-example-queries"></a>

以下示例显示了您可以从 Athena 对解析程序查询日志执行的一些查询。

## 示例 1 – 按降序 query\$1timestamp 顺序查询日志
<a name="querying-r53-resolver-logs-example-1-query-logs-in-descending-query_timestamp-order"></a>

以下查询以降序 `query_timestamp` 顺序显示日志结果。

```
SELECT * FROM "r53_rlogs"
ORDER BY query_timestamp DESC
```

## 示例 2 – 指定的开始时间和结束时间内的查询日志
<a name="querying-r53-resolver-logs-example-2-query-logs-within-specified-start-and-end-times"></a>

以下查询查询在 2020 年 9 月 24 日午夜和上午 8 点之间日志。根据您自己的要求替换开始时间和结束时间。

```
SELECT query_timestamp, srcids.instance, srcaddr, srcport, query_name, rcode
FROM "r53_rlogs"
WHERE (parse_datetime(query_timestamp,'yyyy-MM-dd''T''HH:mm:ss''Z')
     BETWEEN parse_datetime('2020-09-24-00:00:00','yyyy-MM-dd-HH:mm:ss') 
     AND parse_datetime('2020-09-24-00:08:00','yyyy-MM-dd-HH:mm:ss'))
ORDER BY query_timestamp DESC
```

## 示例 3 – 基于指定 DNS 查询名称模式的查询日志
<a name="querying-r53-resolver-logs-example-3-query-logs-based-on-a-specified-dns-query-name-pattern"></a>

以下查询选择其查询名称包含字符串“example.com”的记录。

```
SELECT query_timestamp, srcids.instance, srcaddr, srcport, query_name, rcode, answers
FROM "r53_rlogs"
WHERE query_name LIKE '%example.com%'
ORDER BY query_timestamp DESC
```

## 示例 4 – 没有答案的查询日志请求
<a name="querying-r53-resolver-logs-example-4-query-log-requests-with-no-answer"></a>

以下查询选择请求未收到任何答案的日志条目。

```
SELECT query_timestamp, srcids.instance, srcaddr, srcport, query_name, rcode, answers
FROM "r53_rlogs"
WHERE cardinality(answers) = 0
```

## 示例 5 – 查询具有特定答案的日志
<a name="querying-r53-resolver-logs-example-5-query-logs-with-a-specific-answer"></a>

以下查询显示了 `answer.Rdata` 值具有指定的 IP 地址的日志。

```
SELECT query_timestamp, srcids.instance, srcaddr, srcport, query_name, rcode, answer.Rdata
FROM "r53_rlogs"
CROSS JOIN UNNEST(r53_rlogs.answers) as st(answer)
WHERE answer.Rdata='203.0.113.16';
```

# 查询 Amazon SES 事件日志
<a name="querying-ses-logs"></a>

您可以使用 Amazon Athena 查询[Amazon Simple Email Service](https://aws.amazon.com/ses/)（Amazon SES）事件日志。

Amazon SES 是一个电子邮件平台，让您能够使用自己的电子邮件地址和域，经济高效且方便地收发电子邮件。您可以使用事件、指标和统计信息更加细致地监控 Amazon SES 发送活动。

您可以根据自己定义的特征，将 Amazon SES 事件发布到 [Amazon CloudWatch](https://aws.amazon.com/cloudwatch/)、[Amazon Data Firehose](https://aws.amazon.com/kinesis/data-firehose/) 或 [Amazon Simple Notification Service](https://aws.amazon.com/sns/)。将信息存储到 Amazon S3 以后，您可以从 Amazon Athena 进行查询。

[有关 Amazon SES 日志的 Athena `CREATE TABLE` 语句示例，包括如何在 Amazon SES 事件日志数据中创建视图和展平嵌套数组的步骤，请参阅 AWS 博客文章 Analyzing Amazon SES event data with AWS Analytics Services](https://aws.amazon.com/blogs/messaging-and-targeting/analyzing-amazon-ses-event-data-with-aws-analytics-services/) 中的“Step 3: Using Amazon Athena to query the SES event logs”。

# 查询 Amazon VPC 流日志
<a name="vpc-flow-logs"></a>

Amazon Virtual Private Cloud 流日志捕获有关在 VPC 中传入和传出网络接口的 IP 流量的信息。可使用日志来调查网络流量模式，并识别 VPC 网络中的威胁和风险。

若要查询 Amazon VPC 流日志，您有两种选择：

****
+ **Amazon VPC 控制台** – 使用 Amazon VPC 控制台中的 Athena 集成功能生成 CloudFormation 模板，用于创建 Athena 数据库、工作组和流日志表并为您进行分区。该模板还会创建一组[预定义的流日志查询](https://docs.aws.amazon.com/vpc/latest/userguide/flow-logs-athena.html#predefined-queries)，可用于获取有关流经 VPC 的流量的洞察。

  有关此方法更多信息，请参阅《*Amazon VPC 用户指南*》中的[使用 Amazon Athena 查询流日志](https://docs.aws.amazon.com/vpc/latest/userguide/flow-logs-athena.html)。
+ **Amazon Athena 控制台** – 直接在 Athena 控制台中创建表和查询。有关更多信息，请继续阅读此页面。

当您开始在 Athena 中查询日志之前，[启用 VPC 流日志](https://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/flow-logs.html)，并将其配置为保存到您的 Amazon S3 存储桶。在您创建日志后，让它们运行几分钟以收集一些数据。这些日志是采用 Athena 允许您直接查询的 GZIP 压缩格式创建的。

创建 VPC 流日志时，当您希望指定在流日志中返回的字段以及这些字段的显示顺序时，请使用自定义格式。有关流日志记录的更多信息，请参阅《*Amazon VPC 用户指南*》中的[流日志记录](https://docs.aws.amazon.com/vpc/latest/userguide/flow-logs.html#flow-log-records)。

## 注意事项和限制
<a name="vpc-flow-logs-common-considerations"></a>

在 Athena 中为 Amazon VPC 流日志创建表时，请记住以下几点：
+ 默认情况下，在 Athena 中，Parquet 将按名称访问列。有关更多信息，请参阅 [处理架构更新](handling-schema-updates-chapter.md)。
+ 使用流日志记录中的名称作为 Athena 中列的名称。Athena 架构中列的名称应与 Amazon VPC 流日志中的字段名称完全匹配，但有以下不同之处：
  + 将 Amazon VPC 日志字段名称中的连字符替换为 Athena 列名称中的下划线。有关 Athena 中可接受的数据库名称、表名以及列名的字符的信息，请参阅[命名数据库、表和列](tables-databases-columns-names.md)。
  + 在 Athena 中，使用反引号将[保留关键字](reserved-words.md)中的流日志记录名称括起来，将其转义。
+ VPC 流日志特定于 AWS 账户。当您将日志文件发布到 Amazon S3 时，在 Amazon S3 中创建的 Amazon VPC 的路径包含用于创建流日志的 AWS 账户 ID。有关更多信息，请参阅 *Amazon VPC 用户指南*中的[将流日志发布到 Amazon S3](https://docs.aws.amazon.com/vpc/latest/userguide/flow-logs-s3.html)。

**Topics**
+ [注意事项和限制](#vpc-flow-logs-common-considerations)
+ [为 Amazon VPC 流日志创建表并对其进行查询](vpc-flow-logs-create-table-statement.md)
+ [以 Apache Parquet 格式为流日志创建表](vpc-flow-logs-parquet.md)
+ [使用分区投影功能创建和查询 Amazon VPC 流日志表](vpc-flow-logs-partition-projection.md)
+ [使用分区投影功能并以 Apache Parquet 格式为流日志创建表](vpc-flow-logs-partition-projection-parquet-example.md)
+ [其他资源](query-examples-vpc-logs-additional-resources.md)

# 为 Amazon VPC 流日志创建表并对其进行查询
<a name="vpc-flow-logs-create-table-statement"></a>

以下过程将为 Amazon VPC 流日志创建 Amazon VPC 表。使用自定义格式创建流日志时，可以创建一个表，其字段与您在创建流日志时指定的字段相匹配，且字段顺序与您指定的字段顺序相同。

**为 Amazon VPC 流日志创建 Athena 表**

1. 按照 [注意事项和限制](vpc-flow-logs.md#vpc-flow-logs-common-considerations) 部分中的准则，在 Athena 控制台查询编辑器中输入类似以下内容的 DDL 语句。此示例语句创建一个表，其中包含 Amazon VPC 流日志版本 2 到 5 的列，如[流日志记录](https://docs.aws.amazon.com/vpc/latest/userguide/flow-logs.html#flow-log-records)中所示。如果使用不同的列集或列顺序，请相应地修改语句。

   ```
   CREATE EXTERNAL TABLE IF NOT EXISTS `vpc_flow_logs` (
     version int,
     account_id string,
     interface_id string,
     srcaddr string,
     dstaddr string,
     srcport int,
     dstport int,
     protocol bigint,
     packets bigint,
     bytes bigint,
     start bigint,
     `end` bigint,
     action string,
     log_status string,
     vpc_id string,
     subnet_id string,
     instance_id string,
     tcp_flags int,
     type string,
     pkt_srcaddr string,
     pkt_dstaddr string,
     region string,
     az_id string,
     sublocation_type string,
     sublocation_id string,
     pkt_src_aws_service string,
     pkt_dst_aws_service string,
     flow_direction string,
     traffic_path int
   )
   PARTITIONED BY (`date` date)
   ROW FORMAT DELIMITED
   FIELDS TERMINATED BY ' '
   LOCATION 's3://amzn-s3-demo-bucket/prefix/AWSLogs/{account_id}/vpcflowlogs/{region_code}/'
   TBLPROPERTIES ("skip.header.line.count"="1");
   ```

   请注意以下几点：
   + 此查询指定 `ROW FORMAT DELIMITED` 并省略指定 SerDe。这意味着查询使用[用于 CSV、TSV 和自定义分隔文件的 Lazy Simple SerDe](lazy-simple-serde.md)。在此查询中，字段由一个空格终止。
   + `PARTITIONED BY` 子句使用 `date` 类型。这样，就可以在查询中使用数学运算符来选择比特定日期更早或更新的内容。
**注意**  
因为 `date` 在 DDL 语句中是保留关键字，所以用反引号字符对其进行转义。有关更多信息，请参阅 [转义查询中的保留关键字](reserved-words.md)。
   + 对于具有不同自定义格式的 VPC 流日志，请修改字段以与您创建流日志时指定的字段相匹配。

1. 修改 `LOCATION 's3://amzn-s3-demo-bucket/prefix/AWSLogs/{account_id}/vpcflowlogs/{region_code}/'` 以指向包含您的日志数据的 Amazon S3 存储桶。

1. 在 Athena 控制台中运行查询。查询完成后，Athena 将注册 `vpc_flow_logs` 表，使其中的数据可以供您发出查询。

1. 创建分区以便能够读取数据，如以下示例查询中所示。此示例查询创建指定日期的单个分区。根据需要替换日期和位置的占位符。
**注意**  
此查询仅为您指定的日期创建单个分区。若要自动执行此过程，请使用运行此查询并以这种方式为 `year/month/day` 创建分区的脚本，或使用 `CREATE TABLE` 语句指定[分区投影](vpc-flow-logs-partition-projection.md)。

   ```
   ALTER TABLE vpc_flow_logs
   ADD PARTITION (`date`='YYYY-MM-dd')
   LOCATION 's3://amzn-s3-demo-bucket/prefix/AWSLogs/{account_id}/vpcflowlogs/{region_code}/YYYY/MM/dd';
   ```

## vpc\$1flow\$1logs 表的查询示例
<a name="query-examples-vpc-logs"></a>

使用 Athena 控制台中的查询编辑器在创建的表上运行 SQL 语句。您可以保存查询、查看之前的查询或下载 CSV 格式的查询结果。在以下示例中，将 `vpc_flow_logs` 替换为表名称。根据您的要求修改列值和其他变量。

以下示例查询列出了指定日期的最多 100 个流日志。

```
SELECT * 
FROM vpc_flow_logs 
WHERE date = DATE('2020-05-04') 
LIMIT 100;
```

以下查询列出所有被拒绝的 TCP 连接并使用新创建的日期分区列 `date` 来从中提取这些事件发生的星期几。

```
SELECT day_of_week(date) AS
  day,
  date,
  interface_id,
  srcaddr,
  action,
  protocol
FROM vpc_flow_logs
WHERE action = 'REJECT' AND protocol = 6
LIMIT 100;
```

若要查看哪个服务器收到最大数量的 HTTPS 请求，请使用以下查询。它计算在 HTTPS 端口 443 上接收的数据包数，按目标 IP 地址对它们进行分组，并返回上一周的前 10 个。

```
SELECT SUM(packets) AS
  packetcount,
  dstaddr
FROM vpc_flow_logs
WHERE dstport = 443 AND date > current_date - interval '7' day
GROUP BY dstaddr
ORDER BY packetcount DESC
LIMIT 10;
```

# 以 Apache Parquet 格式为流日志创建表
<a name="vpc-flow-logs-parquet"></a>

以下过程将以 Apache Parquet 格式为 Amazon VPC 流日志创建 Amazon VPC 表。

**以 Parquet 格式为 Amazon VPC 流日志创建 Athena 表**

1. 按照 [注意事项和限制](vpc-flow-logs.md#vpc-flow-logs-common-considerations) 部分中的准则，在 Athena 控制台查询编辑器中输入类似以下内容的 DDL 语句。以下示例语句创建一个表，其中包含 Amazon VPC 流日志版本 2 到 5 的列，如 Parquet 格式的[流日志记录](https://docs.aws.amazon.com/vpc/latest/userguide/flow-logs.html#flow-log-records)中所示，Hive 按小时分区。如果您没有按小时分区，请删除 `PARTITIONED BY` 子句中的 `hour`。

   ```
   CREATE EXTERNAL TABLE IF NOT EXISTS vpc_flow_logs_parquet (
     version int,
     account_id string,
     interface_id string,
     srcaddr string,
     dstaddr string,
     srcport int,
     dstport int,
     protocol bigint,
     packets bigint,
     bytes bigint,
     start bigint,
     `end` bigint,
     action string,
     log_status string,
     vpc_id string,
     subnet_id string,
     instance_id string,
     tcp_flags int,
     type string,
     pkt_srcaddr string,
     pkt_dstaddr string,
     region string,
     az_id string,
     sublocation_type string,
     sublocation_id string,
     pkt_src_aws_service string,
     pkt_dst_aws_service string,
     flow_direction string,
     traffic_path int
   )
   PARTITIONED BY (
     `aws-account-id` string,
     `aws-service` string,
     `aws-region` string,
     `year` string, 
     `month` string, 
     `day` string,
     `hour` string
   )
   ROW FORMAT SERDE 
     'org.apache.hadoop.hive.ql.io.parquet.serde.ParquetHiveSerDe'
   STORED AS INPUTFORMAT 
     'org.apache.hadoop.hive.ql.io.parquet.MapredParquetInputFormat' 
   OUTPUTFORMAT 
     'org.apache.hadoop.hive.ql.io.parquet.MapredParquetOutputFormat'
   LOCATION
     's3://amzn-s3-demo-bucket/prefix/AWSLogs/'
   TBLPROPERTIES (
     'EXTERNAL'='true', 
     'skip.header.line.count'='1'
     )
   ```

1. 修改示例 `LOCATION 's3://amzn-s3-demo-bucket/prefix/AWSLogs/'` 以指向包含您的日志数据的 Amazon S3 路径。

1. 在 Athena 控制台中运行查询。

1. 如果数据采用 Hive 兼容格式，请在 Athena 控制台中运行以下命令，更新并加载元存储中的 Hive 分区。查询完成后，可以查询 `vpc_flow_logs_parquet` 表中的数据。

   ```
   MSCK REPAIR TABLE vpc_flow_logs_parquet
   ```

   如果您使用的不是 Hive 兼容数据，请运行 [ALTER TABLE ADD PARTITION](alter-table-add-partition.md) 以加载分区。

有关使用 Athena 查询 Parquet 格式 Amazon VPC 流日志的更多信息，请参阅 *AWS 大数据博客*中的文章 [使用 Apache Pparquet 格式的 VPC 流日志优化性能并降低网络分析成本](https://aws.amazon.com/blogs/big-data/optimize-performance-and-reduce-costs-for-network-analytics-with-vpc-flow-logs-in-apache-parquet-format/)。

# 使用分区投影功能创建和查询 Amazon VPC 流日志表
<a name="vpc-flow-logs-partition-projection"></a>

使用如下所示的 `CREATE TABLE` 语句创建表、对表进行分区并使用[分区投影](partition-projection.md)自动填充分区。将示例中的表名称 `test_table_vpclogs` 替换为您的表名称。编辑 `LOCATION` 子句以指定包含 Amazon VPC 日志数据的 Amazon S3 存储桶。

以下 `CREATE TABLE` 语句适用于以非 Hive 样式的分区格式传送的 VPC 流日志。该示例支持多账户聚合。要将来自多个账户的 VPC 流日志集中到一个 Amazon S3 存储桶中，必须在 Amazon S3 路径中输入账户 ID。

```
CREATE EXTERNAL TABLE IF NOT EXISTS test_table_vpclogs (
  version int,
  account_id string,
  interface_id string,
  srcaddr string,
  dstaddr string,
  srcport int,
  dstport int,
  protocol bigint,
  packets bigint,
  bytes bigint,
  start bigint,
  `end` bigint,
  action string,
  log_status string,
  vpc_id string,
  subnet_id string,
  instance_id string,
  tcp_flags int,
  type string,
  pkt_srcaddr string,
  pkt_dstaddr string,
  az_id string,
  sublocation_type string,
  sublocation_id string,
  pkt_src_aws_service string,
  pkt_dst_aws_service string,
  flow_direction string,
  traffic_path int
)
PARTITIONED BY (accid string, region string, day string)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY ' '
LOCATION '$LOCATION_OF_LOGS'
TBLPROPERTIES
(
"skip.header.line.count"="1",
"projection.enabled" = "true",
"projection.accid.type" = "enum",
"projection.accid.values" = "$ACCID_1,$ACCID_2",
"projection.region.type" = "enum",
"projection.region.values" = "$REGION_1,$REGION_2,$REGION_3",
"projection.day.type" = "date",
"projection.day.range" = "$START_RANGE,NOW",
"projection.day.format" = "yyyy/MM/dd",
"storage.location.template" = "s3://amzn-s3-demo-bucket/AWSLogs/${accid}/vpcflowlogs/${region}/${day}"
)
```

## test\$1table\$1vpclogs 表的查询示例
<a name="query-examples-vpc-logs-pp"></a>

以下示例查询将查询之前 `CREATE TABLE` 语句创建的 `test_table_vpclogs`。将查询中的 `test_table_vpclogs` 替换为您自己的表名称。根据您的要求修改列值和其他变量。

若要在指定时间段内按时间顺序返回前 100 个访问日志条目，请运行如下所示的查询。

```
SELECT *
FROM test_table_vpclogs
WHERE day >= '2021/02/01' AND day < '2021/02/28'
ORDER BY day ASC
LIMIT 100
```

若要查看哪个服务器在指定时间段内接收前十个 HTTP 数据包，请运行如下所示的查询。该查询计算在 HTTPS 端口 443 上接收的数据包数，按目标 IP 地址对其进行分组，并返回上一周的前 10 个条目。

```
SELECT SUM(packets) AS packetcount, 
       dstaddr
FROM test_table_vpclogs
WHERE dstport = 443
  AND day >= '2021/03/01'
  AND day < '2021/03/31'
GROUP BY dstaddr
ORDER BY packetcount DESC
LIMIT 10
```

若要返回在指定时间段内创建的日志，请运行如下所示的查询。

```
SELECT interface_id,
       srcaddr,
       action,
       protocol,
       to_iso8601(from_unixtime(start)) AS start_time,
       to_iso8601(from_unixtime("end")) AS end_time
FROM test_table_vpclogs
WHERE DAY >= '2021/04/01'
  AND DAY < '2021/04/30'
```

若要返回指定时间段内源 IP 地址的访问日志，请运行如下所示的查询。

```
SELECT *
FROM test_table_vpclogs
WHERE srcaddr = '10.117.1.22'
  AND day >= '2021/02/01'
  AND day < '2021/02/28'
```

若要列出已被拒绝的 TCP 连接，请运行如下所示的查询。

```
SELECT day,
       interface_id,
       srcaddr,
       action,
       protocol
FROM test_table_vpclogs
WHERE action = 'REJECT' AND protocol = 6 AND day >= '2021/02/01' AND day < '2021/02/28'
LIMIT 10
```

若要返回以 `10.117` 开头的 IP 地址范围的访问日志，请运行如下所示的查询。

```
SELECT *
FROM test_table_vpclogs
WHERE split_part(srcaddr,'.', 1)='10'
  AND split_part(srcaddr,'.', 2) ='117'
```

若要返回某个时间范围内目标 IP 地址的访问日志，请运行如下所示的查询。

```
SELECT *
FROM test_table_vpclogs
WHERE dstaddr = '10.0.1.14'
  AND day >= '2021/01/01'
  AND day < '2021/01/31'
```

# 使用分区投影功能并以 Apache Parquet 格式为流日志创建表
<a name="vpc-flow-logs-partition-projection-parquet-example"></a>

以下用于 VPC 流日志的分区投影 `CREATE TABLE` 语句采用 Apache Parquet 格式，不兼容 Hive，按小时和日期而不是按天分区。将示例中的表名称 `test_table_vpclogs_parquet` 替换为您的表名称。编辑 `LOCATION` 子句以指定包含 Amazon VPC 日志数据的 Amazon S3 存储桶。

```
CREATE EXTERNAL TABLE IF NOT EXISTS test_table_vpclogs_parquet (
  version int,
  account_id string,
  interface_id string,
  srcaddr string,
  dstaddr string,
  srcport int,
  dstport int,
  protocol bigint,
  packets bigint,
  bytes bigint,
  start bigint,
  `end` bigint,
  action string,
  log_status string,
  vpc_id string,
  subnet_id string,
  instance_id string,
  tcp_flags int,
  type string,
  pkt_srcaddr string,
  pkt_dstaddr string,
  az_id string,
  sublocation_type string,
  sublocation_id string,
  pkt_src_aws_service string,
  pkt_dst_aws_service string,
  flow_direction string,
  traffic_path int
)
PARTITIONED BY (region string, date string, hour string)
ROW FORMAT SERDE
'org.apache.hadoop.hive.ql.io.parquet.serde.ParquetHiveSerDe'
STORED AS INPUTFORMAT
'org.apache.hadoop.hive.ql.io.parquet.MapredParquetInputFormat'
OUTPUTFORMAT
'org.apache.hadoop.hive.ql.io.parquet.MapredParquetOutputFormat'
LOCATION 's3://amzn-s3-demo-bucket/prefix/AWSLogs/{account_id}/vpcflowlogs/'
TBLPROPERTIES (
"EXTERNAL"="true",
"skip.header.line.count" = "1",
"projection.enabled" = "true",
"projection.region.type" = "enum",
"projection.region.values" = "us-east-1,us-west-2,ap-south-1,eu-west-1",
"projection.date.type" = "date",
"projection.date.range" = "2021/01/01,NOW",
"projection.date.format" = "yyyy/MM/dd",
"projection.hour.type" = "integer",
"projection.hour.range" = "00,23",
"projection.hour.digits" = "2",
"storage.location.template" = "s3://amzn-s3-demo-bucket/prefix/AWSLogs/${account_id}/vpcflowlogs/${region}/${date}/${hour}"
)
```

# 其他资源
<a name="query-examples-vpc-logs-additional-resources"></a>

有关使用 Athena 分析 VPC 流日志的更多信息，请参阅以下 AWS 大数据博客文章：
+ [通过点击式 Amazon Athena 集成分析 VPC 流日志](https://aws.amazon.com/blogs/networking-and-content-delivery/analyze-vpc-flow-logs-with-point-and-click-amazon-athena-integration/) 
+ [使用 Amazon Athena 和 Quick 分析 VPC 流日志](https://aws.amazon.com/blogs/big-data/analyzing-vpc-flow-logs-using-amazon-athena-and-amazon-quicksight/)
+ [使用 Apache Parquet 格式的 VPC 流日志优化性能并降低网络分析成本](https://aws.amazon.com/blogs/big-data/optimize-performance-and-reduce-costs-for-network-analytics-with-vpc-flow-logs-in-apache-parquet-format/)

# 查询 AWS WAF日志
<a name="waf-logs"></a>

AWS WAF 是一个 Web 应用程序防火墙，可以监视和控制受保护的 Web 应用程序从客户端收到的 HTTP 和 HTTPS 请求。您可以通过在 AWS WAF Web 访问控制列表（ACL）中配置规则来定义如何处理 Web 请求。然后，您可以通过将 Web ACL 关联到 Web 应用程序来保护该应用程序。您可以使用 AWS WAF 保护的 Web 应用程序资源的示例包括 Amazon CloudFront 分配、Amazon API Gateway REST API 和应用程序负载均衡器。有关 AWS WAF 的更多信息，请参阅*《AWS WAF Developer Guide》*中的 [AWS WAF](https://docs.aws.amazon.com/waf/latest/developerguide/waf-chapter.html)。

AWS WAF 日志包含您的 Web ACL 所分析的流量相关信息，例如 AWS WAF 从 AWS 资源收到请求的时间，有关请求的详细信息，以及每个请求所匹配的规则的操作。

您可以配置 AWS WAF Web ACL 将日志发布到多个目标中的一个目标，即您可以查询和查看这些日志的地方。有关配置 Web ACL 日志记录和 AWS WAF 日志内容的更多信息，请参阅*《AWS WAF Developer Guide》*中的 [Logging AWS WAF web ACL traffic](https://docs.aws.amazon.com/waf/latest/developerguide/logging.html)。

有关如何使用 Athena 分析 AWS WAF 日志以深入了解威胁检测和潜在安全攻击的信息，请参阅 AWS 网络和内容交付博客文章 [How to use Amazon Athena queries to analyze AWS WAF logs and provide the visibility needed for threat detection](https://aws.amazon.com/blogs/networking-and-content-delivery/how-to-use-amazon-athena-queries-to-analyze-aws-waf-logs-and-provide-the-visibility-needed-for-threat-detection/)。

有关如何将 AWS WAF 日志聚合到中央数据湖存储库并使用 Athena 进行查询的示例，请参阅 AWS 大数据博客文章 [Analyzing AWS WAF logs with OpenSearch Service, Amazon Athena, and Quick](https://aws.amazon.com/blogs/big-data/analyzing-aws-waf-logs-with-amazon-es-amazon-athena-and-amazon-quicksight/)。

本主题提供了分区投影、手动分区和不使用任何分区的 `CREATE TABLE` 语句示例。

**注意**  
本主题中的 `CREATE TABLE` 语句可以用于 v1 和 v2 AWS WAF 日志。在 v1 中，`webaclid` 字段包含一个 ID。在 v2 中，`webaclid` 字段包含完整的 ARN。这里的 `CREATE TABLE` 语句通过使用 `string` 数据类型未知地处理此内容。

**Topics**
+ [使用分区投影为 Athena 中的 AWS WAF S3 日志创建表](create-waf-table-partition-projection.md)
+ [使用手动分区为 Athena 中的 AWS WAF S3 日志创建表](create-waf-table-manual-partition.md)
+ [创建不进行分区的 AWS WAF 日志表](create-waf-table.md)
+ [AWS WAF 日志的示例查询](query-examples-waf-logs.md)

# 使用分区投影为 Athena 中的 AWS WAF S3 日志创建表
<a name="create-waf-table-partition-projection"></a>

由于 AWS WAF 日志具有您可以预先指定其分区方案的已知结构，因此您可以使用 Athena [分区投影](partition-projection.md)功能减少查询运行时间并自动管理分区。当添加新数据时，分区投影会自动添加新分区。这样就不必使用 `ALTER TABLE ADD PARTITION` 手动添加分区了。

以下示例 `CREATE TABLE` 语句会自动在 AWS WAF 日志上从指定日期开始到当前日期为止，为四个不同 AWS 区域使用分区投影。本示例中的 `PARTITION BY` 子句按区域和日期进行分区，但您可以根据自己的要求修改此子句。根据需要修改字段以匹配您的日志输出。在 `LOCATION` 和 `storage.location.template` 子句中，将 *amzn-s3-demo-bucket* 和 *AWS\$1ACCOUNT\$1NUMBER* 占位符替换为值，该值标识 AWS WAF 日志在 Amazon S3 存储桶中的位置。对于 `projection.day.range`，将 *2021*/*01*/*01* 替换为要使用的开始日期。成功运行查询后，您可以查询表。您无需运行 `ALTER TABLE ADD PARTITION` 来加载分区。

```
CREATE EXTERNAL TABLE `waf_logs_partition_projection`(
  `timestamp` bigint, 
  `formatversion` int, 
  `webaclid` string, 
  `terminatingruleid` string, 
  `terminatingruletype` string, 
  `action` string, 
  `terminatingrulematchdetails` array<struct<conditiontype:string,sensitivitylevel:string,location:string,matcheddata:array<string>>>, 
  `httpsourcename` string, 
  `httpsourceid` string, 
  `rulegrouplist` array<struct<rulegroupid:string,terminatingrule:struct<ruleid:string,action:string,rulematchdetails:array<struct<conditiontype:string,sensitivitylevel:string,location:string,matcheddata:array<string>>>>,nonterminatingmatchingrules:array<struct<ruleid:string,action:string,overriddenaction:string,rulematchdetails:array<struct<conditiontype:string,sensitivitylevel:string,location:string,matcheddata:array<string>>>,challengeresponse:struct<responsecode:string,solvetimestamp:string>,captcharesponse:struct<responsecode:string,solvetimestamp:string>>>,excludedrules:string>>, 
  `ratebasedrulelist` array<struct<ratebasedruleid:string,limitkey:string,maxrateallowed:int>>, 
  `nonterminatingmatchingrules` array<struct<ruleid:string,action:string,rulematchdetails:array<struct<conditiontype:string,sensitivitylevel:string,location:string,matcheddata:array<string>>>,challengeresponse:struct<responsecode:string,solvetimestamp:string>,captcharesponse:struct<responsecode:string,solvetimestamp:string>>>, 
  `requestheadersinserted` array<struct<name:string,value:string>>, 
  `responsecodesent` string, 
  `httprequest` struct<clientip:string,country:string,headers:array<struct<name:string,value:string>>,uri:string,args:string,httpversion:string,httpmethod:string,requestid:string,fragment:string,scheme:string,host:string>,
  `labels` array<struct<name:string>>, 
  `captcharesponse` struct<responsecode:string,solvetimestamp:string,failurereason:string>, 
  `challengeresponse` struct<responsecode:string,solvetimestamp:string,failurereason:string>, 
  `ja3fingerprint` string, 
  `ja4fingerprint` string, 
  `oversizefields` string, 
  `requestbodysize` int, 
  `requestbodysizeinspectedbywaf` int)
  PARTITIONED BY ( 
   `log_time` string)
ROW FORMAT SERDE 
  'org.openx.data.jsonserde.JsonSerDe' 
STORED AS INPUTFORMAT 
  'org.apache.hadoop.mapred.TextInputFormat' 
OUTPUTFORMAT 
  'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'
LOCATION
  's3://amzn-s3-demo-bucket/AWSLogs/AWS_ACCOUNT_NUMBER/WAFLogs/cloudfront/testui/'
TBLPROPERTIES (
 'projection.enabled'='true',
  'projection.log_time.format'='yyyy/MM/dd/HH/mm',
  'projection.log_time.interval'='1',
  'projection.log_time.interval.unit'='minutes',
  'projection.log_time.range'='2025/01/01/00/00,NOW',
  'projection.log_time.type'='date',
  'storage.location.template'='s3://amzn-s3-demo-bucket/AWSLogs/AWS_ACCOUNT_NUMBER/WAFLogs/cloudfront/testui/${log_time}')
```

**注意**  
示例中 `LOCATION` 子句中的路径格式是标准格式，但可能因所实施的 AWS WAF 配置而异。例如，以下示例 AWS WAF 日志路径适用于 CloudFront 分配：  

```
s3://amzn-s3-demo-bucket/AWSLogs/AWS_ACCOUNT_NUMBER/WAFLogs/cloudfront/cloudfronyt/2025/01/01/00/00/
```
如果您在创建或查询 AWS WAF 日志表时遇到问题，请确认日志数据位置或联系 [支持](https://console.aws.amazon.com/support/home/)。

更多有关分区投影的信息，请参阅 [将分区投影与 Amazon Athena 结合使用](partition-projection.md)。

# 使用手动分区为 Athena 中的 AWS WAF S3 日志创建表
<a name="create-waf-table-manual-partition"></a>

本节介绍了如何使用手动分区来为 AWS WAF 日志创建表。

在 `LOCATION` 和 `storage.location.template` 子句中，将 *amzn-s3-demo-bucket* 和 *AWS\$1ACCOUNT\$1NUMBER* 占位符替换为值，该值标识 AWS WAF 日志在 Amazon S3 存储桶中的位置。

```
CREATE EXTERNAL TABLE `waf_logs_manual_partition`(
  `timestamp` bigint, 
  `formatversion` int, 
  `webaclid` string, 
  `terminatingruleid` string, 
  `terminatingruletype` string, 
  `action` string, 
  `terminatingrulematchdetails` array<struct<conditiontype:string,sensitivitylevel:string,location:string,matcheddata:array<string>>>, 
  `httpsourcename` string, 
  `httpsourceid` string, 
  `rulegrouplist` array<struct<rulegroupid:string,terminatingrule:struct<ruleid:string,action:string,rulematchdetails:array<struct<conditiontype:string,sensitivitylevel:string,location:string,matcheddata:array<string>>>>,nonterminatingmatchingrules:array<struct<ruleid:string,action:string,overriddenaction:string,rulematchdetails:array<struct<conditiontype:string,sensitivitylevel:string,location:string,matcheddata:array<string>>>,challengeresponse:struct<responsecode:string,solvetimestamp:string>,captcharesponse:struct<responsecode:string,solvetimestamp:string>>>,excludedrules:string>>, 
  `ratebasedrulelist` array<struct<ratebasedruleid:string,limitkey:string,maxrateallowed:int>>, 
  `nonterminatingmatchingrules` array<struct<ruleid:string,action:string,rulematchdetails:array<struct<conditiontype:string,sensitivitylevel:string,location:string,matcheddata:array<string>>>,challengeresponse:struct<responsecode:string,solvetimestamp:string>,captcharesponse:struct<responsecode:string,solvetimestamp:string>>>, 
  `requestheadersinserted` array<struct<name:string,value:string>>, 
  `responsecodesent` string, 
  `httprequest` struct<clientip:string,country:string,headers:array<struct<name:string,value:string>>,uri:string,args:string,httpversion:string,httpmethod:string,requestid:string,fragment:string,scheme:string,host:string>, 
  `labels` array<struct<name:string>>, 
  `captcharesponse` struct<responsecode:string,solvetimestamp:string,failurereason:string>, 
  `challengeresponse` struct<responsecode:string,solvetimestamp:string,failurereason:string>, 
  `ja3fingerprint` string, 
  `ja4fingerprint` string, 
  `oversizefields` string, 
  `requestbodysize` int, 
  `requestbodysizeinspectedbywaf` int)
  PARTITIONED BY ( `year` string, `month` string, `day` string, `hour` string, `min` string)
ROW FORMAT SERDE 
  'org.openx.data.jsonserde.JsonSerDe' 
STORED AS INPUTFORMAT 
  'org.apache.hadoop.mapred.TextInputFormat' 
OUTPUTFORMAT 
  'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'
LOCATION
  's3://amzn-s3-demo-bucket/AWSLogs/AWS_ACCOUNT_NUMBER/WAFLogs/cloudfront/webacl/'
```

# 创建不进行分区的 AWS WAF 日志表
<a name="create-waf-table"></a>

本节介绍如何创建不进行分区或分区投影的 AWS WAF 日志表。

**注意**  
出于性能和成本原因，不建议使用非分区架构进行查询。有关更多信息，请参阅 AWS 大数据博客中的 [Top 10 Performance Tuning Tips for Amazon Athena](https://aws.amazon.com/blogs/big-data/top-10-performance-tuning-tips-for-amazon-athena/)（Amazon Athena 的十大性能优化技巧）。

**创建 AWS WAF 表**

1. 将以下 DDL 语句复制并粘贴到 Athena 控制台中。根据需要修改字段以匹配您的日志输出。修改 Amazon S3 存储桶的 `LOCATION` 以对应用于存储日志的存储桶。

   此查询使用 [OpenX JSON SerDe](openx-json-serde.md)。
**注意**  
SerDe 期望每个 JSON 文档都位于单行文本中，并且不使用行终止字符分隔记录中的字段。如果 JSON 文本采用美观的打印格式，当您在创建表后尝试对其进行查询时，可能会收到类似以下内容的错误消息：HIVE\$1CURSOR\$1ERROR: Row is not a valid JSON Object（HIVE\$1CURSOR\$1ERROR：行不是有效的 JSON 对象）或 HIVE\$1CURSOR\$1ERROR: JsonParseException: Unexpected end-of-input: expected close marker for OBJECT（HIVE\$1CURSOR\$1ERROR：JsonParseException：意外的输入结束：对象的预期关闭标记）。有关更多信息，请参阅 GitHub 上 OpenX SerDe 文档中的 [JSON 数据文件](https://github.com/rcongiu/Hive-JSON-Serde#json-data-files)。

   ```
   CREATE EXTERNAL TABLE `waf_logs`(
     `timestamp` bigint,
     `formatversion` int,
     `webaclid` string,
     `terminatingruleid` string,
     `terminatingruletype` string,
     `action` string,
     `terminatingrulematchdetails` array <
                                       struct <
                                           conditiontype: string,
                                           sensitivitylevel: string,
                                           location: string,
                                           matcheddata: array < string >
                                             >
                                        >,
     `httpsourcename` string,
     `httpsourceid` string,
     `rulegrouplist` array <
                         struct <
                             rulegroupid: string,
                             terminatingrule: struct <
                                                 ruleid: string,
                                                 action: string,
                                                 rulematchdetails: array <
                                                                      struct <
                                                                          conditiontype: string,
                                                                          sensitivitylevel: string,
                                                                          location: string,
                                                                          matcheddata: array < string >
                                                                             >
                                                                       >
                                                   >,
                             nonterminatingmatchingrules: array <
                                                                 struct <
                                                                     ruleid: string,
                                                                     action: string,
                                                                     overriddenaction: string,
                                                                     rulematchdetails: array <
                                                                                          struct <
                                                                                              conditiontype: string,
                                                                                              sensitivitylevel: string,
                                                                                              location: string,
                                                                                              matcheddata: array < string >
                                                                                                 >
                                                                      >,
                                                                     challengeresponse: struct <
                                                                               responsecode: string,
                                                                               solvetimestamp: string
                                                                                 >,
                                                                     captcharesponse: struct <
                                                                               responsecode: string,
                                                                               solvetimestamp: string
                                                                                 >
                                                                       >
                                                                >,
                             excludedrules: string
                               >
                          >,
   `ratebasedrulelist` array <
                            struct <
                                ratebasedruleid: string,
                                limitkey: string,
                                maxrateallowed: int
                                  >
                             >,
     `nonterminatingmatchingrules` array <
                                       struct <
                                           ruleid: string,
                                           action: string,
                                           rulematchdetails: array <
                                                                struct <
                                                                    conditiontype: string,
                                                                    sensitivitylevel: string,
                                                                    location: string,
                                                                    matcheddata: array < string >
                                                                       >
                                                                >,
                                           challengeresponse: struct <
                                                               responsecode: string,
                                                               solvetimestamp: string
                                                                >,
                                           captcharesponse: struct <
                                                               responsecode: string,
                                                               solvetimestamp: string
                                                                >
                                             >
                                        >,
     `requestheadersinserted` array <
                                   struct <
                                       name: string,
                                       value: string
                                         >
                                    >,
     `responsecodesent` string,
     `httprequest` struct <
                       clientip: string,
                       country: string,
                       headers: array <
                                   struct <
                                       name: string,
                                       value: string
                                         >
                                    >,
                       uri: string,
                       args: string,
                       httpversion: string,
                       httpmethod: string,
                       requestid: string
                         >,
     `labels` array <
                  struct <
                      name: string
                        >
                   >,
     `captcharesponse` struct <
                           responsecode: string,
                           solvetimestamp: string,
                           failureReason: string
                             >,
     `challengeresponse` struct <
                           responsecode: string,
                           solvetimestamp: string,
                           failureReason: string
                           >,
     `ja3Fingerprint` string,
     `oversizefields` string,
     `requestbodysize` int,
     `requestbodysizeinspectedbywaf` int
   )
   ROW FORMAT SERDE 'org.openx.data.jsonserde.JsonSerDe'
   STORED AS INPUTFORMAT 'org.apache.hadoop.mapred.TextInputFormat'
   OUTPUTFORMAT 'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'
   LOCATION 's3://amzn-s3-demo-bucket/prefix/'
   ```

1. 在 Athena 控制台查询编辑器中运行 `CREATE EXTERNAL TABLE` 语句。这将注册 `waf_logs` 表，并使其中的数据可用于来自 Athena 的查询。

# AWS WAF 日志的示例查询
<a name="query-examples-waf-logs"></a>

本部分中许多示例查询使用之前创建的分区投影表。根据您的要求修改示例中的表名称、列值和其他变量。若要提高查询的性能并降低成本，请在筛选条件中添加分区列。

**Topics**
+ [统计引用站点、IP 地址或匹配的规则](query-examples-waf-logs-count.md)
+ [使用日期和时间进行查询](query-examples-waf-logs-date-time.md)
+ [查询被阻止的请求或地址](query-examples-waf-logs-blocked-requests.md)

# 统计引用站点、IP 地址或匹配的规则
<a name="query-examples-waf-logs-count"></a>

本部分中的示例查询相关日志项的计数。
+ [Count the number of referrers that contain a specified term](#waf-example-count-referrers-with-specified-term)
+ [Count all matched IP addresses in the last 10 days that have matched excluded rules](#waf-example-count-matched-ip-addresses)
+ [Group all counted managed rules by the number of times matched](#waf-example-group-managed-rules-by-times-matched)
+ [Group all counted custom rules by number of times matched](#waf-example-group-custom-rules-by-times-matched)

**Example – 统计包含指定术语的引用站点数量**  
以下查询计算指定日期范围内包含“amazon”一词的引用者数量。  

```
WITH test_dataset AS 
  (SELECT header FROM waf_logs
    CROSS JOIN UNNEST(httprequest.headers) AS t(header) WHERE "date" >= '2021/03/01'
    AND "date" < '2021/03/31')
SELECT COUNT(*) referer_count 
FROM test_dataset 
WHERE LOWER(header.name)='referer' AND header.value LIKE '%amazon%'
```

**Example – 统计过去 10 天内与排除规则匹配的所有匹配 IP 地址**  
以下查询计算过去 10 天内 IP 地址与规则组中排除规则匹配的次数。  

```
WITH test_dataset AS 
  (SELECT * FROM waf_logs 
    CROSS JOIN UNNEST(rulegrouplist) AS t(allrulegroups))
SELECT 
  COUNT(*) AS count, 
  "httprequest"."clientip", 
  "allrulegroups"."excludedrules",
  "allrulegroups"."ruleGroupId"
FROM test_dataset 
WHERE allrulegroups.excludedrules IS NOT NULL AND from_unixtime(timestamp/1000) > now() - interval '10' day
GROUP BY "httprequest"."clientip", "allrulegroups"."ruleGroupId", "allrulegroups"."excludedrules"
ORDER BY count DESC
```

**Example - 按匹配次数对所有已计数的托管规则进行分组**  
如果您在 2022 年 10 月 27 日之前在 Web ACL 配置中将规则组规则操作设置为“计数”，AWS WAF 在 Web ACL JSON 中将覆盖内容保存为 `excludedRules`。现在，用于将规则替换为“计数”的 JSON 设置位于 `ruleActionOverrides` 设置中。有关更多信息，请参阅《AWS WAF 开发人员指南》**中的[规则组中的操作覆盖](https://docs.aws.amazon.com/waf/latest/developerguide/web-acl-rule-group-override-options.html)。要从新的日志结构中提取计数模式下的托管规则，请在 `ruleGroupList` 部分而不是 `excludedRules` 字段中查询 `nonTerminatingMatchingRules`，如下例所示。  

```
SELECT
 count(*) AS count,
 httpsourceid,
 httprequest.clientip,
 t.rulegroupid, 
 t.nonTerminatingMatchingRules
FROM "waf_logs" 
CROSS JOIN UNNEST(rulegrouplist) AS t(t) 
WHERE action <> 'BLOCK' AND cardinality(t.nonTerminatingMatchingRules) > 0 
GROUP BY t.nonTerminatingMatchingRules, action, httpsourceid, httprequest.clientip, t.rulegroupid 
ORDER BY "count" DESC 
Limit 50
```

**Example - 按匹配次数对所有已计数的自定义规则进行分组**  
以下查询按匹配次数对所有已计数的自定义规则进行分组。  

```
SELECT
  count(*) AS count,
         httpsourceid,
         httprequest.clientip,
         t.ruleid,
         t.action
FROM "waf_logs" 
CROSS JOIN UNNEST(nonterminatingmatchingrules) AS t(t) 
WHERE action <> 'BLOCK' AND cardinality(nonTerminatingMatchingRules) > 0 
GROUP BY t.ruleid, t.action, httpsourceid, httprequest.clientip 
ORDER BY "count" DESC
Limit 50
```

有关自定义规则和托管规则组的日志位置的信息，请参阅《AWS WAF 开发人员指南》**中的[监控和调整](https://docs.aws.amazon.com/waf/latest/developerguide/web-acl-testing-activities.html)。

# 使用日期和时间进行查询
<a name="query-examples-waf-logs-date-time"></a>

本部分中的示例包括使用日期和时间值的查询。
+ [Return the timestamp field in human-readable ISO 8601 format](#waf-example-return-human-readable-timestamp)
+ [Return records from the last 24 hours](#waf-example-return-records-last-24-hours)
+ [Return records for a specified date range and IP address](#waf-example-return-records-date-range-and-ip)
+ [For a specified date range, count the number of IP addresses in five minute intervals](#waf-example-count-ip-addresses-in-date-range)
+ [Count the number of X-Forwarded-For IP in the last 10 days](#waf-example-count-x-forwarded-for-ip)

**Example – 以人类可读 ISO 8601 格式返回时间戳字段**  
以下查询使用 `from_unixtime` 和 `to_iso8601` 函数以人类可读的 ISO 8601 格式返回 `timestamp` 字段（例如 `2019-12-13T23:40:12.000Z` 而不是 `1576280412771`）。该查询还返回 HTTP 源名称、源 ID 和请求。  

```
SELECT to_iso8601(from_unixtime(timestamp / 1000)) as time_ISO_8601,
       httpsourcename,
       httpsourceid,
       httprequest
FROM waf_logs
LIMIT 10;
```

**Example – 返回过去 24 小时的记录**  
以下查询使用 `WHERE` 子句中的筛选条件返回过去 24 小时内记录的 HTTP 源名称、HTTP 源 ID 和 HTTP 请求字段。  

```
SELECT to_iso8601(from_unixtime(timestamp/1000)) AS time_ISO_8601, 
       httpsourcename, 
       httpsourceid, 
       httprequest 
FROM waf_logs
WHERE from_unixtime(timestamp/1000) > now() - interval '1' day
LIMIT 10;
```

**Example – 返回指定日期范围和 IP 地址的记录**  
以下查询列出了指定的客户端 IP 地址在指定日期范围内的记录。  

```
SELECT * 
FROM waf_logs 
WHERE httprequest.clientip='53.21.198.66' AND "date" >= '2021/03/01' AND "date" < '2021/03/31'
```

**Example – 对于指定的日期范围，计算在五分钟间隔内的 IP 地址数**  
对于特定日期范围，以下查询计算在五分钟间隔内的 IP 地址数。  

```
WITH test_dataset AS 
  (SELECT 
     format_datetime(from_unixtime((timestamp/1000) - ((minute(from_unixtime(timestamp / 1000))%5) * 60)),'yyyy-MM-dd HH:mm') AS five_minutes_ts,
     "httprequest"."clientip" 
     FROM waf_logs 
     WHERE "date" >= '2021/03/01' AND "date" < '2021/03/31')
SELECT five_minutes_ts,"clientip",count(*) ip_count 
FROM test_dataset 
GROUP BY five_minutes_ts,"clientip"
```

**Example – 计算过去 10 天内 X-Forwarded-For IP 的数量**  
以下查询将筛选请求标头，并统计过去 10 天内 X-Forwarded-For IP 的数量。  

```
WITH test_dataset AS
  (SELECT header
   FROM waf_logs
   CROSS JOIN UNNEST (httprequest.headers) AS t(header)
   WHERE from_unixtime("timestamp"/1000) > now() - interval '10' DAY) 
SELECT header.value AS ip,
       count(*) AS COUNT 
FROM test_dataset 
WHERE header.name='X-Forwarded-For' 
GROUP BY header.value 
ORDER BY COUNT DESC
```

有关日期和时间函数的更多信息，请参阅 Trino 文档中的 [日期与时间函数和运算符](https://trino.io/docs/current/functions/datetime.html)。

# 查询被阻止的请求或地址
<a name="query-examples-waf-logs-blocked-requests"></a>

本部分中的示例查询被阻止的请求或地址。
+ [Extract the top 100 IP addresses blocked by a specified rule type](#waf-example-extract-top-100-blocked-ip-by-rule)
+ [Count the number of times a request from a specified country has been blocked](#waf-example-count-request-blocks-from-country)
+ [Count the number of times a request has been blocked, grouping by specific attributes](#waf-example-count-request-blocks-by-attribute)
+ [Count the number of times a specific terminating rule ID has been matched](#waf-example-count-terminating-rule-id-matches)
+ [Retrieve the top 100 IP addresses blocked during a specified date range](#waf-example-top-100-ip-addresses-blocked-for-date-range)

**Example – 提取被指定规则类型阻止的前 100 个 IP 地址**  
下面的查询将提取并统计在指定的日期范围内被 `RATE_BASED` 终止规则阻止的前 100 个 IP 地址。  

```
SELECT COUNT(httpRequest.clientIp) as count,
httpRequest.clientIp
FROM waf_logs
WHERE terminatingruletype='RATE_BASED' AND action='BLOCK' and "date" >= '2021/03/01'
AND "date" < '2021/03/31'
GROUP BY httpRequest.clientIp
ORDER BY count DESC
LIMIT 100
```

**Example – 计算来自指定国家/地区的请求被阻止的次数**  
以下查询针对来自属于爱尔兰 (IE) IP 地址的请求，计算请求到达但被 `RATE_BASED` 终止规则阻止的次数。  

```
SELECT 
  COUNT(httpRequest.country) as count, 
  httpRequest.country 
FROM waf_logs
WHERE 
  terminatingruletype='RATE_BASED' AND 
  httpRequest.country='IE'
GROUP BY httpRequest.country
ORDER BY count
LIMIT 100;
```

**Example – 计算请求被阻止的次数，按特定属性分组**  
以下查询计算请求被阻止的次数，并按照 WebACL、RuleId、ClientIP 和 HTTP 请求 URI 对结果分组。  

```
SELECT 
  COUNT(*) AS count,
  webaclid,
  terminatingruleid,
  httprequest.clientip,
  httprequest.uri
FROM waf_logs
WHERE action='BLOCK'
GROUP BY webaclid, terminatingruleid, httprequest.clientip, httprequest.uri
ORDER BY count DESC
LIMIT 100;
```

**Example – 计算特定终止规则 ID 匹配的次数**  
以下查询计算特定终止规则 ID 匹配的次数 (`WHERE terminatingruleid='e9dd190d-7a43-4c06-bcea-409613d9506e'`)。然后，查询按照 WebACL、操作、ClientIP 和 HTTP 请求 URI 对结果分组。  

```
SELECT 
  COUNT(*) AS count,
  webaclid,
  action,
  httprequest.clientip,
  httprequest.uri
FROM waf_logs
WHERE terminatingruleid='e9dd190d-7a43-4c06-bcea-409613d9506e'
GROUP BY webaclid, action, httprequest.clientip, httprequest.uri
ORDER BY count DESC
LIMIT 100;
```

**Example – 检索指定日期范围内被阻止的前 100 个 IP 地址**  
以下查询将提取在指定日期范围内被阻止的前 100 个 IP 地址。该查询还列出了 IP 地址被阻止的次数。  

```
SELECT "httprequest"."clientip", "count"(*) "ipcount", "httprequest"."country"
FROM waf_logs
WHERE "action" = 'BLOCK' and "date" >= '2021/03/01'
AND "date" < '2021/03/31'
GROUP BY "httprequest"."clientip", "httprequest"."country"
ORDER BY "ipcount" DESC limit 100
```

有关查询 Amazon S3 日志的更多信息，请参阅以下主题：
+ AWS 知识中心中的[如何使用 Athena 分析 Amazon S3 服务器访问日志？](https://aws.amazon.com/premiumsupport/knowledge-center/analyze-logs-athena/)
+ 《Amazon Simple Storage Service 用户指南》中的[使用 Amazon Athena 查询请求的 Amazon S3 访问日志](https://docs.aws.amazon.com/AmazonS3/latest/dev/using-s3-access-logs-to-identify-requests.html#querying-s3-access-logs-for-requests)
+ 《Amazon Simple Storage Service 用户指南》中的[使用 AWS CloudTrail 识别 Amazon S3 请求](https://docs.aws.amazon.com/AmazonS3/latest/dev/cloudtrail-request-identification.html)

# 查询 Amazon S3 中存储的 Web 服务器日志
<a name="querying-web-server-logs"></a>

您可以使用 Athena 查询 Amazon S3 中存储的 Web 服务器日志。本部分中的主题介绍如何在 Athena 中创建表，以查询各种格式的 Web 服务器日志。

**Topics**
+ [查询存储在 Amazon S3 中的 Apache 日志](querying-apache-logs.md)
+ [查询存储在 Amazon S3 中的互联网信息服务器（IIS）日志](querying-iis-logs.md)

# 查询存储在 Amazon S3 中的 Apache 日志
<a name="querying-apache-logs"></a>

您可以使用 Amazon Athena 以查询存储在 Amazon S3 账户中的 [Apache HTTP 服务器日志文件](https://httpd.apache.org/docs/2.4/logs.html)。本主题介绍如何创建表架构以查询常见日志格式的 Apache [访问日志](https://httpd.apache.org/docs/2.4/logs.html#accesslog)文件。

常见日志格式中的字段包括客户端 IP 地址、客户端 ID、用户 ID、接收请求的时间戳、客户端请求的文本、服务器状态代码以及返回给客户端的对象的大小。

以下示例数据显示了 Apache 的常见日志格式。

```
198.51.100.7 - Li [10/Oct/2019:13:55:36 -0700] "GET /logo.gif HTTP/1.0" 200 232
198.51.100.14 - Jorge [24/Nov/2019:10:49:52 -0700] "GET /index.html HTTP/1.1" 200 2165
198.51.100.22 - Mateo [27/Dec/2019:11:38:12 -0700] "GET /about.html HTTP/1.1" 200 1287
198.51.100.9 - Nikki [11/Jan/2020:11:40:11 -0700] "GET /image.png HTTP/1.1" 404 230
198.51.100.2 - Ana [15/Feb/2019:10:12:22 -0700] "GET /favicon.ico HTTP/1.1" 404 30
198.51.100.13 - Saanvi [14/Mar/2019:11:40:33 -0700] "GET /intro.html HTTP/1.1" 200 1608
198.51.100.11 - Xiulan [22/Apr/2019:10:51:34 -0700] "GET /group/index.html HTTP/1.1" 200 1344
```

## 在 Athena 中为 Apache 日志创建表
<a name="querying-apache-logs-creating-a-table-in-athena"></a>

必须先为 Athena 创建一个表架构，以便它能够读取日志数据，然后才能查询存储在 Amazon S3 中的 Apache 日志。若要为 Apache 日志创建 Athena 表，可以使用 [Grok SerDe](grok-serde.md)。有关使用 Grok SerDe 的更多信息，请参阅《AWS Glue 开发人员指南[https://docs.aws.amazon.com/glue/latest/dg/custom-classifier.html#custom-classifier-grok](https://docs.aws.amazon.com/glue/latest/dg/custom-classifier.html#custom-classifier-grok)》中的 *编写 Grok 自定义分类器*。

**要在 Athena 中为 Apache Web 服务器日志创建表**

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

1. 将以下 DDL 语句粘贴到 Athena 查询编辑器中。修改 `LOCATION 's3://amzn-s3-demo-bucket/apache-log-folder/'` 中的值以指向 Amazon S3 中的 Apache 日志。

   ```
   CREATE EXTERNAL TABLE apache_logs (
     client_ip string,
     client_id string,
     user_id string,
     request_received_time string,
     client_request string,
     server_status string,
     returned_obj_size string
     )
   ROW FORMAT SERDE
      'com.amazonaws.glue.serde.GrokSerDe'
   WITH SERDEPROPERTIES (
      'input.format'='^%{IPV4:client_ip} %{DATA:client_id} %{USERNAME:user_id} %{GREEDYDATA:request_received_time} %{QUOTEDSTRING:client_request} %{DATA:server_status} %{DATA: returned_obj_size}$'
      )
   STORED AS INPUTFORMAT
      'org.apache.hadoop.mapred.TextInputFormat'
   OUTPUTFORMAT
      'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'
   LOCATION
      's3://amzn-s3-demo-bucket/apache-log-folder/';
   ```

1. 在 Athena 控制台中运行查询以注册 `apache_logs` 表。查询完成后，调查结果准备就绪，可供您从 Athena 查询。

### 示例查询
<a name="querying-apache-logs-example-select-queries"></a>

**Example – 筛选 404 错误**  
以下的示例查询从 `apache_logs` 表中选择了请求接收时间、客户端请求的文本以及服务器状态代码。HTTP 状态代码 `404`（未找到页面）的 `WHERE` 子句筛选条件。  

```
SELECT request_received_time, client_request, server_status
FROM apache_logs
WHERE server_status = '404'
```
下图显示了 Athena 查询编辑器中的查询结果。  

![\[从 Athena 查询 Apache 日志的 HTTP 404 条目。\]](http://docs.aws.amazon.com/zh_cn/athena/latest/ug/images/querying-apache-logs-1.png)


**Example – 筛选成功的请求**  
以下的示例查询从 `apache_logs` 表中选择了用户 ID、请求接收时间、客户端请求的文本以及服务器状态代码。HTTP 状态代码 `200`（成功）的子句筛选条件 `WHERE`。  

```
SELECT user_id, request_received_time, client_request, server_status
FROM apache_logs
WHERE server_status = '200'
```
下图显示了 Athena 查询编辑器中的查询结果。  

![\[从 Athena 查询 Apache 日志的 HTTP 200 条目。\]](http://docs.aws.amazon.com/zh_cn/athena/latest/ug/images/querying-apache-logs-2.png)


**Example – 按时间戳筛选**  
如下示例将查询请求接收时间大于指定时间戳的记录。  

```
SELECT * FROM apache_logs WHERE request_received_time > 10/Oct/2023:00:00:00
```

# 查询存储在 Amazon S3 中的互联网信息服务器（IIS）日志
<a name="querying-iis-logs"></a>

您可以使用 Amazon Athena 查询存储在您 Amazon S3 账户中的 Microsoft 互联网信息服务 (IIS) Web 服务器日志。虽然 IIS 使用[各种不同](https://docs.microsoft.com/en-us/previous-versions/iis/6.0-sdk/ms525807(v%3Dvs.90))的日志文件格式，本主题将介绍如何创建表架构以从 Athena 查询 W3C 扩展日志和 IIS 日志文件格式日志。

由于 W3C 扩展和 IIS 日志文件格式使用单字符分隔符（分别为空格和逗号），并且没有位于引号中的值，因此您可以使用 [LazySimpleSerDe](lazy-simple-serde.md) 为其创建 Athena 表。

**Topics**
+ [查询 W3C 扩展日志文件格式](querying-iis-logs-w3c-extended-log-file-format.md)
+ [查询 IIS 日志文件格式](querying-iis-logs-iis-log-file-format.md)
+ [查询 NCSA 日志文件格式](querying-iis-logs-ncsa-log-file-format.md)

# 查询 W3C 扩展日志文件格式
<a name="querying-iis-logs-w3c-extended-log-file-format"></a>

[W3C 扩展](https://docs.microsoft.com/en-us/windows/win32/http/w3c-logging)日志文件数据格式具有空格分隔的字段。W3C 扩展日志中显示的字段由 Web 服务器管理员决定，后者将选择要包含哪些日志字段。以下示例日志数据具有 `date, time`、`c-ip`、`s-ip`、`cs-method`、`cs-uri-stem`、`sc-status`、`sc-bytes`、`cs-bytes`、`time-taken` 和 `cs-version` 字段。

```
2020-01-19 22:48:39 203.0.113.5 198.51.100.2 GET /default.html 200 540 524 157 HTTP/1.0
2020-01-19 22:49:40 203.0.113.10 198.51.100.12 GET /index.html 200 420 324 164 HTTP/1.0
2020-01-19 22:50:12 203.0.113.12 198.51.100.4 GET /image.gif 200 324 320 358 HTTP/1.0
2020-01-19 22:51:44 203.0.113.15 198.51.100.16 GET /faq.html 200 330 324 288 HTTP/1.0
```

## 在 Athena 中为 W3C 扩展日志创建表
<a name="querying-iis-logs-creating-a-table-in-athena-for-w3c-extended-logs"></a>

在查询 W3C 扩展日志之前，必须先创建表架构，以便 Athena 可以读取日志数据。

**要在 Athena 中为 W3C 扩展日志创建表**

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

1. 将类似以下内容的 DDL 语句粘贴到 Athena 控制台中，并注意以下几点：

   1. 在示例中添加或删除列，以便与要查询的日志中的字段对应。

   1. W3C 扩展日志文件格式的列名称包含连字符 (`-`)。然而，根据 [Athena 命名约定](tables-databases-columns-names.md)，示例 `CREATE TABLE` 语句将用下划线 (`_`) 替换连字符。

   1. 要指定空格分隔符，请使用 `FIELDS TERMINATED BY ' '`。

   1. 修改 `LOCATION 's3://amzn-s3-demo-bucket/w3c-log-folder/'` 中的值以指向您在 Amazon S3 中的 W3C 扩展日志。

   ```
   CREATE EXTERNAL TABLE `iis_w3c_logs`( 
     date_col string, 
     time_col string, 
     c_ip string,
     s_ip string,
     cs_method string, 
     cs_uri_stem string, 
     sc_status string,
     sc_bytes string,
     cs_bytes string,
     time_taken string,
     cs_version string
     ) 
   ROW FORMAT DELIMITED  
     FIELDS TERMINATED BY ' '  
   STORED AS INPUTFORMAT  
     'org.apache.hadoop.mapred.TextInputFormat'  
   OUTPUTFORMAT  
     'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat' 
   LOCATION   's3://amzn-s3-demo-bucket/w3c-log-folder/'
   ```

1. 在 Athena 控制台中运行查询以注册 `iis_w3c_logs` 表。查询完成后，调查结果准备就绪，可供您从 Athena 查询。

## 示例 W3C 扩展日志选择查询
<a name="querying-iis-logs-example-w3c-extended-log-select-query"></a>

以下示例查询从表 `iis_w3c_logs` 中选择了请求的日期、时间、请求目标和用时。`WHERE` 子句筛选条件，用于 HTTP 方法为 `GET`，以及 HTTP 状态代码为 `200`（成功）的情况。

```
SELECT date_col, time_col, cs_uri_stem, time_taken
FROM iis_w3c_logs
WHERE cs_method = 'GET' AND sc_status = '200'
```

下图显示了 Athena 查询编辑器中的查询结果。

![\[存储在 Amazon S3 中的 W3C 扩展日志文件的 Athena 示例查询结果。\]](http://docs.aws.amazon.com/zh_cn/athena/latest/ug/images/querying-iis-logs-1.png)


## 合并日期和时间字段
<a name="querying-iis-logs-example-w3c-extended-log-combining-date-and-time"></a>

以空格分隔的 `date` 和 `time` 字段是日志源数据中的单独条目，但您可以根据需要将它们合并到时间戳中。在 [SELECT](select.md) 或者 [CREATE TABLE AS SELECT](create-table-as.md) 查询中使用 [concat()](https://prestodb.io/docs/current/functions/string.html#concat) 和 [date\$1parse()](https://prestodb.io/docs/current/functions/datetime.html#date_parse) 函数来连接日期和时间列并将其转换为时间戳格式。以下示例使用 CTAS 查询创建一个新表，其中包含 `derived_timestamp` 列。

```
CREATE TABLE iis_w3c_logs_w_timestamp AS
SELECT 
  date_parse(concat(date_col,' ', time_col),'%Y-%m-%d %H:%i:%s') as derived_timestamp, 
  c_ip, 
  s_ip, 
  cs_method, 
  cs_uri_stem, 
  sc_status, 
  sc_bytes, 
  cs_bytes, 
  time_taken, 
  cs_version
FROM iis_w3c_logs
```

创建表后，您可以直接查询新的时间戳列，如以下示例所示。

```
SELECT derived_timestamp, cs_uri_stem, time_taken
FROM iis_w3c_logs_w_timestamp
WHERE cs_method = 'GET' AND sc_status = '200'
```

下图显示了查询的结果。

![\[具有派生时间戳列的表上的 W3C 扩展日志文件查询结果。\]](http://docs.aws.amazon.com/zh_cn/athena/latest/ug/images/querying-iis-logs-1a.png)


# 查询 IIS 日志文件格式
<a name="querying-iis-logs-iis-log-file-format"></a>

与 W3C 扩展格式不同，[IIS 日志文件格式](https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2003/cc728311(v%3dws.10))有一组固定的字段，并包含逗号作为分隔符。LazySimpleSerDe 将逗号视为分隔符，逗号后的空格视为下一个字段的开头。

以下示例以 IIS 日志文件格式显示示例数据。

```
203.0.113.15, -, 2020-02-24, 22:48:38, W3SVC2, SERVER5, 198.51.100.4, 254, 501, 488, 200, 0, GET, /index.htm, -, 
203.0.113.4, -, 2020-02-24, 22:48:39, W3SVC2, SERVER6, 198.51.100.6, 147, 411, 388, 200, 0, GET, /about.html, -, 
203.0.113.11, -, 2020-02-24, 22:48:40, W3SVC2, SERVER7, 198.51.100.18, 170, 531, 468, 200, 0, GET, /image.png, -, 
203.0.113.8, -, 2020-02-24, 22:48:41, W3SVC2, SERVER8, 198.51.100.14, 125, 711, 868, 200, 0, GET, /intro.htm, -,
```

## 在 Athena 中为 IIS 日志文件创建表
<a name="querying-iis-logs-creating-a-table-in-athena-for-iis-log-files"></a>

要在 Amazon S3 中查询 IIS 日志文件格式日志，请首先创建一个表架构，以便 Athena 可以读取日志数据。

**要在 Athena 中为 IIS 日志文件格式日志创建表**

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

1. 将以下 DDL 语句粘贴到 Athena 控制台中，并注意以下几点：

   1. 要指定逗号分隔符，请使用 `FIELDS TERMINATED BY ','`。

   1. 修改 LOCATION 's3://amzn-s3-demo-bucket/*iis-log-file-folder*/' 中的值以指向 Amazon S3 中的 IIS 日志格式日志文件。

   ```
   CREATE EXTERNAL TABLE `iis_format_logs`(
   client_ip_address string,
   user_name string,
   request_date string,
   request_time string,
   service_and_instance string,
   server_name string,
   server_ip_address string,
   time_taken_millisec string,
   client_bytes_sent string,
   server_bytes_sent string,
   service_status_code string,
   windows_status_code string,
   request_type string,
   target_of_operation string,
   script_parameters string
      )
   ROW FORMAT DELIMITED
     FIELDS TERMINATED BY ','
   STORED AS INPUTFORMAT
     'org.apache.hadoop.mapred.TextInputFormat'
   OUTPUTFORMAT
     'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'
   LOCATION
     's3://amzn-s3-demo-bucket/iis-log-file-folder/'
   ```

1. 在 Athena 控制台中运行查询以注册 `iis_format_logs` 表。查询完成后，调查结果准备就绪，可供您从 Athena 查询。

## IIS 日志格式选择查询示例
<a name="querying-iis-logs-example-iis-log-format-select-query"></a>

以下示例查询从表 `iis_format_logs` 中选择了请求日期、请求时间、请求目标和用时（以毫秒为单位）。`WHERE` 子句筛选条件，用于请求类型为 `GET`，以及 HTTP 状态代码为 `200`（成功）的情况。在查询中，请注意，在 `' GET'` 和 `' 200'` 中需要前导空白才能成功查询。

```
SELECT request_date, request_time, target_of_operation, time_taken_millisec
FROM iis_format_logs
WHERE request_type = ' GET' AND service_status_code = ' 200'
```

下图显示了示例数据查询的结果。

![\[存储在 Amazon S3 中的 IIS 日志文件格式日志文件的 Athena 示例查询结果。\]](http://docs.aws.amazon.com/zh_cn/athena/latest/ug/images/querying-iis-logs-2.png)


# 查询 NCSA 日志文件格式
<a name="querying-iis-logs-ncsa-log-file-format"></a>

IIS 还使用 [NCSA 日志记录](https://docs.microsoft.com/en-us/windows/win32/http/ncsa-logging)格式，该格式具有固定数量的 ASCII 文本格式的字段，以空格分隔。该结构与用于 Apache 访问日志的常用日志格式类似。NCSA 常用日志数据格式中的字段包括客户端 IP 地址、客户端 ID（通常不使用）、域\$1用户 ID、接收请求的时间戳、客户端请求的文本、服务器状态代码以及返回给客户端的对象的大小。

以下示例显示了 IIS 所记录的 NCSA 常用日志格式的数据。

```
198.51.100.7 - ExampleCorp\Li [10/Oct/2019:13:55:36 -0700] "GET /logo.gif HTTP/1.0" 200 232
198.51.100.14 - AnyCompany\Jorge [24/Nov/2019:10:49:52 -0700] "GET /index.html HTTP/1.1" 200 2165
198.51.100.22 - ExampleCorp\Mateo [27/Dec/2019:11:38:12 -0700] "GET /about.html HTTP/1.1" 200 1287
198.51.100.9 - AnyCompany\Nikki [11/Jan/2020:11:40:11 -0700] "GET /image.png HTTP/1.1" 404 230
198.51.100.2 - ExampleCorp\Ana [15/Feb/2019:10:12:22 -0700] "GET /favicon.ico HTTP/1.1" 404 30
198.51.100.13 - AnyCompany\Saanvi [14/Mar/2019:11:40:33 -0700] "GET /intro.html HTTP/1.1" 200 1608
198.51.100.11 - ExampleCorp\Xiulan [22/Apr/2019:10:51:34 -0700] "GET /group/index.html HTTP/1.1" 200 1344
```

## 在 Athena 中为 IIS NCSA 日志创建表
<a name="querying-iis-logs-ncsa-creating-a-table-in-athena"></a>

对于您的 `CREATE TABLE` 语句，则可以使用 [Grok SerDe](grok-serde.md) 和一个类似于 [Apache Web 服务器日志](querying-apache-logs.md)模式的 grok 模式。与 Apache 日志不同，grok 模式使用 `%{DATA:user_id}` 而不是 `%{USERNAME:user_id}` 作为第三个字段来考虑 `domain\user_id` 中反斜杠的存在。有关使用 Grok SerDe 的更多信息，请参阅《AWS Glue 开发人员指南[https://docs.aws.amazon.com/glue/latest/dg/custom-classifier.html#custom-classifier-grok](https://docs.aws.amazon.com/glue/latest/dg/custom-classifier.html#custom-classifier-grok)》中的 *编写 Grok 自定义分类器*。

**要在 Athena 中为 IIS NCSA Web 服务器日志创建表**

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

1. 将以下 DDL 语句粘贴到 Athena 查询编辑器中。修改 `LOCATION 's3://amzn-s3-demo-bucket/iis-ncsa-logs/'` 中的值以指向 Amazon S3 中的 IIS NCSA 日志。

   ```
   CREATE EXTERNAL TABLE iis_ncsa_logs(
     client_ip string,
     client_id string,
     user_id string,
     request_received_time string,
     client_request string,
     server_status string,
     returned_obj_size string
     )
   ROW FORMAT SERDE
      'com.amazonaws.glue.serde.GrokSerDe'
   WITH SERDEPROPERTIES (
      'input.format'='^%{IPV4:client_ip} %{DATA:client_id} %{DATA:user_id} %{GREEDYDATA:request_received_time} %{QUOTEDSTRING:client_request} %{DATA:server_status} %{DATA: returned_obj_size}$'
      )
   STORED AS INPUTFORMAT
      'org.apache.hadoop.mapred.TextInputFormat'
   OUTPUTFORMAT
      'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'
   LOCATION
      's3://amzn-s3-demo-bucket/iis-ncsa-logs/';
   ```

1. 在 Athena 控制台中运行查询以注册 `iis_ncsa_logs` 表。查询完成后，调查结果准备就绪，可供您从 Athena 查询。

## IIS NCSA 日志的选择查询示例
<a name="querying-iis-logs-ncsa-example-select-queries"></a>

**Example – 筛选 404 错误**  
以下的示例查询从 `iis_ncsa_logs` 表中选择了请求接收时间、客户端请求的文本以及服务器状态代码。HTTP 状态代码 `404`（未找到页面）的 `WHERE` 子句筛选条件。  

```
SELECT request_received_time, client_request, server_status
FROM iis_ncsa_logs
WHERE server_status = '404'
```
下图显示了 Athena 查询编辑器中的查询结果。  

![\[从 Athena 查询 IIS NCSA 日志的 HTTP 404 条目。\]](http://docs.aws.amazon.com/zh_cn/athena/latest/ug/images/querying-iis-logs-3.png)


**Example – 筛选来自特定域的成功请求**  
以下的示例查询从 `iis_ncsa_logs` 表中选择了用户 ID、请求接收时间、客户端请求的文本以及服务器状态代码。`WHERE` 子句筛选来自 `AnyCompany` 域中用户且具有 HTTP 状态代码 `200`（成功）的请求。  

```
SELECT user_id, request_received_time, client_request, server_status
FROM iis_ncsa_logs
WHERE server_status = '200' AND user_id LIKE 'AnyCompany%'
```
下图显示了 Athena 查询编辑器中的查询结果。  

![\[从 Athena 查询 IIS NCSA 日志的 HTTP 200 条目。\]](http://docs.aws.amazon.com/zh_cn/athena/latest/ug/images/querying-iis-logs-4.png)
