

# 处理架构更新
<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`。