

本文為英文版的機器翻譯版本，如內容有任何歧義或不一致之處，概以英文版為準。

# 處理結構描述更新
<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` 陳述式將資料欄新增在現有資料欄之後，但在分割區資料欄之前。

下列範例會在任何分割區資料欄之前的 `comment` 資料表結尾新增 `orders_parquet` 資料欄：

```
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 資料，並以相同順序傳回資料。它不使用資料欄名稱將資料映射到資料欄，因此您可以重新命名 CSV 或 TSV 格式的資料欄，而不會破壞 Athena 查詢。

重新命名資料欄的一種策略是根據相同的基礎資料建立一個新資料表，但使用新的資料欄名稱。下列範例建立新的 `orders_parquet` 資料表，名為 `orders_parquet_column_renamed`。此範例會變更資料欄 ``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 蟲程式可讓您在具有不同結構描述的分割區中探索資料。這表示如果您使用 在 Athena 中建立資料表 AWS Glue，則在爬蟲程式完成處理後，資料表及其分割區的結構描述可能會不同。
+ 如果您直接使用 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 會比較資料表的結構描述與分割區結構描述。如果您在 Athena 中使用 AWS Glue 爬蟲程式在 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)。