

# 处理时间戳数据
<a name="data-types-timestamps"></a>

本节介绍在 Athena 中处理时间戳数据的一些注意事项。

**注意**  
之前引擎版本和 Athena 引擎版本 3 对时间戳的处理方式发生更改。有关 Athena 引擎版本 3 中可能出现的时间戳相关错误以及建议的解决方案的信息，请参阅 [Athena 引擎版本 3](engine-versions-reference-0003.md) 参考中的 [时间戳更改](engine-versions-reference-0003.md#engine-versions-reference-0003-timestamp-changes)。

## 向 Amazon S3 对象写入时间戳数据的格式
<a name="data-types-timestamps-writing-to-s3-objects"></a>

向 Amazon S3 对象写入时间戳数据的格式取决于列数据类型和您使用的 [SerDe 库](https://docs.aws.amazon.com/athena/latest/ug/supported-serdes.html)。
+ 如果您的表列类型为 `DATE`，Athena 预计数据的相应列或属性是 ISO 格式 `YYYY-MM-DD` 的字符串，或者是内置的日期类型（例如 Parquet 或 ORC 的日期类型）。
+ 如果您的表列类型为 `TIME`，Athena 预计数据的相应列或属性是 ISO 格式 `HH:MM:SS` 的字符串，或者是内置的时间类型（例如 Parquet 或 ORC 的日期类型）。
+ 如果您的表列类型为 `TIMESTAMP`，Athena 预计数据的相应列或属性是 `YYYY-MM-DD HH:MM:SS.SSS` 格式的字符串（注意日期和时间之前的空格），或者是内置的时间类型（例如 Parquet、ORC 或 Ion 的时间类型）。请注意，Athena 不保证无效时间戳的行为（例如：`0000-00-00 08:00:00.000`）。
**注意**  
OpenCSVSerDe 时间戳是个例外，必须编码为毫秒解析的 UNIX 纪元。

## 确保时间分区数据与记录中的时间戳字段相匹配
<a name="data-types-timestamps-time-partitioned-data-and-timestamp-fields"></a>

数据的创建者必须确保分区值与分区内的数据一致。例如，若数据具有 `timestamp` 属性，并且使用 Firehose 将数据加载到 Amazon S3 中，则您必须使用[动态分区](https://docs.aws.amazon.com/firehose/latest/dev/dynamic-partitioning.html)，因为 Firehose 的默认分区基于挂钟。

## 使用字符串作为分区键的数据类型
<a name="data-types-timestamps-partition-key-types"></a>

出于性能考虑，最好将 `STRING` 用作分区键的数据类型。尽管在您使用 `DATE` 类型时，Athena 会将 `YYYY-MM-DD` 格式的分区值识别为日期，但这可能会导致性能不佳。因此，我们建议您为分区键改用 `STRING` 数据类型。

## 如何为同时按时间分区的时间戳字段编写查询
<a name="data-types-timestamps-how-to-write-queries-for-timestamp-fields-that-are-also-time-partitioned"></a>

如何为按时间分区的时间戳字段编写查询取决于要查询的表的类型。

### Hive 表
<a name="data-types-timestamps-hive-tables"></a>

由于 Athena 中最常用的 Hive 表，因此查询引擎对列和分区键之间的关系一无所知。因此，您必须始终在查询中为列和分区键添加谓词。

例如，假设您有一 `event_time` 列和一个 `event_date` 分区键，并且想要查询 23:00 到 03:00 之间的事件。在本例中，您必须在查询中同时包含列和分区键的谓词，如下例所示。

```
WHERE event_time BETWEEN start_time AND end_time 
  AND event_date BETWEEN start_time_date AND end_time_date
```

### Iceberg 表
<a name="data-types-timestamps-iceberg-tables"></a>

使用 Iceberg 表，您可以使用计算出的分区值，从而简化查询。例如，假设您的 Iceberg 表是使用如下的 `PARTITIONED BY` 子句创建的：

```
PARTITIONED BY (event_date month(event_time))
```

在本例中，查询引擎会根据 `event_time` 谓词的值自动修剪分区。因此，您的查询只需要为 `event_time` 指定谓词，如以下示例所示。

```
WHERE event_time BETWEEN start_time AND end_time
```

有关更多信息，请参阅 [创建 Iceberg 表](querying-iceberg-creating-tables.md)。

将 Iceberg 隐藏分区功能用于时间戳列时，Iceberg 可能会在从时间戳列派生的构造表列上创建一个分区，并将其转换为日期以确保更有效的分区。例如，可能会从时间戳列 `event_time` 创建 `event_date` 并自动依据 `event_date` 进行分区。在这种情况下，分区**类型**将为**日期**。

为确保使用分区时的最佳查询性能，请按全天范围进行筛选以支持谓词下推。例如，以下查询不会被下推，因为该范围无法转换为单个日期分区，即使在同一天内亦不例外：

```
WHERE event_time >= TIMESTAMP '2024-04-18 00:00:00' AND event_time < TIMESTAMP '2024-04-18 12:00:00'
```

而应使用全天范围来支持谓词向下推并提高查询性能，如下例所示。

```
WHERE event_time >= TIMESTAMP '2024-04-18 00:00:00' AND event_time < TIMESTAMP '2024-04-19 00:00:00'
```

您也可以使用 `BETWEEN start_time AND end_time` 语法或使用多天范围，只要时间戳部分为 `00:00:00` 即可。

有关更多信息，请参阅 [Trino 博客文章](https://trino.io/blog/2023/04/11/date-predicates.html)。