

# Amazon Data Firehose 示例
<a name="partition-projection-kinesis-firehose-example"></a>

当您使用 Firehose 将数据传输到 Amazon S3 时，默认配置会使用类似于以下示例的键写入对象：

```
s3://amzn-s3-demo-bucket/prefix/yyyy/MM/dd/HH/file.extension
```

要创建在查询时自动查找分区的 Athena 表，而不必在新数据到达时将它们添加到 AWS Glue Data Catalog，您可以使用分区投影。

以下 `CREATE TABLE` 示例使用了默认 Firehose 配置。

```
CREATE EXTERNAL TABLE my_ingested_data (
 ...
)
...
PARTITIONED BY (
 datehour STRING
)
LOCATION "s3://amzn-s3-demo-bucket/prefix/"
TBLPROPERTIES (
 "projection.enabled" = "true",
 "projection.datehour.type" = "date",
 "projection.datehour.format" = "yyyy/MM/dd/HH",
 "projection.datehour.range" = "2021/01/01/00,NOW",
 "projection.datehour.interval" = "1",
 "projection.datehour.interval.unit" = "HOURS",
 "storage.location.template" = "s3://amzn-s3-demo-bucket/prefix/${datehour}/"
)
```

`CREATE TABLE` 语句中的 `TBLPROPERTIES` 子句告诉 Athena 以下内容：
+ 查询表时使用分区投影
+ 分区键 `datehour` 的类型为 `date`（包括可选时间）
+ 如何格式化日期
+ 日期时间范围 请注意，必须使用逗号来分隔值，而不是使用连字符。
+ Amazon S3 上数据所在的位置。

当您查询表时，Athena 会计算 `datehour` 值并使用存储位置模板生成分区位置列表。

**Topics**
+ [如何使用 `date` 类型](partition-projection-kinesis-firehose-example-using-the-date-type.md)
+ [如何选择分区键](partition-projection-kinesis-firehose-example-choosing-partition-keys.md)
+ [如何使用自定义前缀和动态分区](partition-projection-kinesis-firehose-example-using-custom-prefixes-and-dynamic-partitioning.md)

# 如何使用 `date` 类型
<a name="partition-projection-kinesis-firehose-example-using-the-date-type"></a>

当您使用投影分区键的 `date` 类型时，您必须指定一个范围。由于在创建 Firehose 传输流之前没有日期数据，您可以使用创建日期作为开始日期。此外，由于您没有将来日期的数据，您可以使用特殊令牌 `NOW` 作为结束日期。

在 `CREATE TABLE` 示例中，开始日期指定为 UTC 时间 2021 年 1 月 1 日午夜。

**注意**  
配置一个与您的数据尽可能匹配的范围，以便 Athena 仅查找现有分区。

在示例表上运行查询时，Athena 将 `datehour` 分区键上的条件与范围结合使用来生成值。请考虑以下查询：

```
SELECT *
FROM my_ingested_data
WHERE datehour >= '2020/12/15/00'
AND datehour < '2021/02/03/15'
```

`SELECT` 查询中的第一个条件使用在 `CREATE TABLE` 语句指定的日期范围开始之前的日期。由于分区投影配置没有为 2021 年 1 月 1 日之前的日期指定分区，因此 Athena 仅在以下位置查找数据，并忽略查询中较早的日期。

```
s3://amzn-s3-demo-bucket/prefix/2021/01/01/00/
s3://amzn-s3-demo-bucket/prefix/2021/01/01/01/
s3://amzn-s3-demo-bucket/prefix/2021/01/01/02/
...
s3://amzn-s3-demo-bucket/prefix/2021/02/03/12/
s3://amzn-s3-demo-bucket/prefix/2021/02/03/13/
s3://amzn-s3-demo-bucket/prefix/2021/02/03/14/
```

同样，如果查询在 2021 年 2 月 3 日 15:00 之前的日期和时间运行，则最后一个分区将反映当前日期和时间，而不是查询条件中的日期和时间。

如果您想查询最新数据，您可以利用 Athena 不生成未来日期的事实，并仅指定起始 `datehour`，如以下示例所示。

```
SELECT *
FROM my_ingested_data
WHERE datehour >= '2021/11/09/00'
```

# 如何选择分区键
<a name="partition-projection-kinesis-firehose-example-choosing-partition-keys"></a>

您可以指定分区投影如何将分区位置映射到分区键。在上一节的 `CREATE TABLE` 示例中，日期和小时组合成一个名为 datehour 的分区键，但也可以使用其他方案。例如，您还可以为年、月、日和小时配置具有单独分区键的表。

不过，将日期拆分为年、月、日意味着无法使用 `date` 分区投影类型。另一种方法是将日期与小时分开，以便仍然利用 `date` 分区投影类型，但要确保用来指定小时范围的查询更加易于阅读。

考虑到这一点，以下 `CREATE TABLE` 示例将日期与小时分开。由于 `date` 是 SQL 中的保留字，因此这一示例使用 `day` 作为表示日期的分区键的名称。

```
CREATE EXTERNAL TABLE my_ingested_data2 (
 ...
)
...
PARTITIONED BY (
 day STRING,
 hour INT
)
LOCATION "s3://amzn-s3-demo-bucket/prefix/"
TBLPROPERTIES (
 "projection.enabled" = "true",
 "projection.day.type" = "date",
 "projection.day.format" = "yyyy/MM/dd",
 "projection.day.range" = "2021/01/01,NOW",
 "projection.day.interval" = "1",
 "projection.day.interval.unit" = "DAYS",
 "projection.hour.type" = "integer",
 "projection.hour.range" = "0,23",
 "projection.hour.digits" = "2",
 "storage.location.template" = "s3://amzn-s3-demo-bucket/prefix/${day}/${hour}/"
)
```

在示例 `CREATE TABLE` 语句中，小时是一个单独的分区键，配置为整数。小时分区键的配置指定了 0 到 23 的范围，并且当 Athena 生成分区位置时，小时的格式应为两位数。

对 `my_ingested_data2` 表的查询可能如下所示：

```
SELECT *
FROM my_ingested_data2
WHERE day = '2021/11/09'
AND hour > 3
```

## 了解分区键和分区投影数据类型
<a name="partition-projection-kinesis-firehose-example-partition-key-types-and-partition-projection-types"></a>

请注意，第一个 `CREATE TABLE` 示例中的 `datehour` 键在分区投影配置中配置为 `date`，但分区键的类型为 `string`。第二个示例中的 `day` 也是如此。分区投影配置中的类型仅告诉 Athena 在生成分区位置时如何格式化值。您指定的类型不会更改分区键的类型 – 在查询中，`datehour` 和 `day` 属于 `string` 类型。

当查询包含与 `day = '2021/11/09'` 类似的条件时，Athena 会使用分区投影配置中指定的日期格式解析表达式右侧的字符串。在 Athena 验证日期是否在配置范围内后，它会再次使用日期格式将日期作为字符串插入到存储位置模板中。

同样，对于像 `day > '2021/11/09'` 这样的查询条件，Athena 会解析右侧并生成配置范围内所有匹配日期的列表。然后使用日期格式将每个日期插入到存储位置模板中以创建分区位置列表。

编写与 `day > '2021-11-09'` 或 `day > DATE '2021-11-09'` 相同的条件将无效。在第一种情况下，日期格式不匹配（注意连字符而不是正斜杠），在第二种情况下，数据类型不匹配。

# 如何使用自定义前缀和动态分区
<a name="partition-projection-kinesis-firehose-example-using-custom-prefixes-and-dynamic-partitioning"></a>

可以使用[自定义前缀](https://docs.aws.amazon.com/firehose/latest/dev/s3-prefixes.html)和[动态分区](https://docs.aws.amazon.com/firehose/latest/dev/dynamic-partitioning.html)配置 Firehose。使用这些功能，您可以配置 Amazon S3 键并设置更好地支持您的使用案例的分区方案。您还可以将分区投影与这些分区方案一起使用，然后进行相应的配置。

例如，您可以使用自定义前缀功能获取具有 ISO 格式日期而非默认 `yyyy/MM/dd/HH` 方案的 Amazon S3 键。

您还可以将自定义前缀与动态分区结合使用，以便从 Firehose 消息中提取 `customer_id` 之类的属性，如下例所示。

```
prefix/!{timestamp:yyyy}-!{timestamp:MM}-!{timestamp:dd}/!{partitionKeyFromQuery:customer_id}/
```

使用该 Amazon S3 前缀，Firehose 传输流会将对象写入 `s3://amzn-s3-demo-bucket/prefix/2021-11-01/customer-1234/file.extension` 等键。对于像 `customer_id` 这样的属性，其值可能事先未知，您可以使用分区投影类型 `injected` 并使用如下所示的 `CREATE TABLE` 语句：

```
CREATE EXTERNAL TABLE my_ingested_data3 (
 ...
)
...
PARTITIONED BY (
 day STRING,
 customer_id STRING
)
LOCATION "s3://amzn-s3-demo-bucket/prefix/"
TBLPROPERTIES (
 "projection.enabled" = "true",
 "projection.day.type" = "date",
 "projection.day.format" = "yyyy-MM-dd",
 "projection.day.range" = "2021-01-01,NOW",
 "projection.day.interval" = "1",
 "projection.day.interval.unit" = "DAYS",
 "projection.customer_id.type" = "injected",
 "storage.location.template" = "s3://amzn-s3-demo-bucket/prefix/${day}/${customer_id}/"
)
```

当您查询具有类型 `injected` 的分区键的表时，您的查询必须包含该分区键的值。对 `my_ingested_data3` 表的查询可能如下所示：

```
SELECT *
FROM my_ingested_data3
WHERE day BETWEEN '2021-11-01' AND '2021-11-30'
AND customer_id = 'customer-1234'
```

## 将 DATE 类型用于日期分区键
<a name="partition-projection-kinesis-firehose-example-iso-formatted-dates"></a>

由于 `day` 分区键的值是 ISO 格式的，您也可以使用 `DATE` 类型而不是 `STRING` 类型作为日期分区键，如以下示例所示：

```
PARTITIONED BY (day DATE, customer_id STRING)
```

查询时，此策略允许您在分区键上使用日期函数而无需解析或转换，如以下示例所示：

```
SELECT *
FROM my_ingested_data3
WHERE day > CURRENT_DATE - INTERVAL '7' DAY
AND customer_id = 'customer-1234'
```

**注意**  
指定 `DATE` 类型的分区键时，假设您已使用[自定义前缀](https://docs.aws.amazon.com/firehose/latest/dev/s3-prefixes.html)功能创建了日期为 ISO 格式的 Amazon S3 键。如果您使用默认的 Firehose 格式 `yyyy/MM/dd/HH`，则即使相应表属性的类型为 `date`，也必须将分区键指定为类型 `string`，如下例所示：  

```
PARTITIONED BY ( 
  `mydate` string)
TBLPROPERTIES (
  'projection.enabled'='true', 
   ...
  'projection.mydate.type'='date',
  'storage.location.template'='s3://amzn-s3-demo-bucket/prefix/${mydate}')
```