【AWS Athena】エラー HIVE_BAD_DATA: Field label's type {A} in parquet is incompatible with type {B} defined in table schema でハマった話


私がAWS AthenaおよびAWS Glueでメタデータの取り扱いでハマった話を共有いたします。

ハマった経緯

  1. parquetのデータをS3に配置する
  2. Glueのテーブルを作成する
  3. AthenaでSELECT文を実行するため、load partition()を実行しパーティションを読み込む
  4. AthenaでSELECT文を実行する → parquet内でのデータ型の定義とGlueテーブルのテーブル定義に齟齬があるためエラーと表示される
  5. テーブル定義を誤っていたことに気づきGlueのテーブル情報を修正する
  6. AthenaでSELECT文を実行する → なぜかparquet内でのデータ型の定義とGlueテーブルのテーブル定義に齟齬があるためエラーと表示される(困惑)

ハマった状況を再現

そのとき、ハマった状況を再現していきます。
まず、S3に配置するparquetをpysparkを使って作成する。

from pyspark.sql.types import StructType
from pyspark.sql.types import StructField
from pyspark.sql.types import StringType
from pyspark.sql.types import IntegerType
rdd = sc.parallelize([
    ('apple', 120),
    ('banana', 220),
    ('tomato', 320),
    ('chicken', 300),
    ])
schema = StructType([
    StructField('label', StringType(), False),
    StructField('value', IntegerType(), False),
    ])
df = spark.createDataFrame(rdd, schema)
df.repartition(1).write.parquet('./output/')

dfの中身はこのようになっております。
labelのカラムがstring型、valueのカラムがint型です。

これをS3に配置します。今回は s3://inu-is-dog/2020-04-19-qiita/id=123/part-00000.snappy.parquet へ配置いたしました。
id=123がパーティションとなるのがミソです。

次にGlueでテーブルを手動で作成いたします。

  • データベース名:sampledb
  • テーブル名:table_20200419
  • データストア:s3://inu-is-dog/2020-04-19-qiita/
  • データ形式:Parquet
  • スキーマ:
    • id(パーティションキー) [string]
    • label [int]
    • value [int]

あえて、スキーマのlabelのデータ型はstring型ではなく、int型(誤り)としています。

次にAthenaの画面に遷移します。
今回はidのパーティションを持つテーブルを作成したため、SELECT文を実行する前にid=123のパーティションを登録する。

ALTER TABLE sampledb.table_20200419 ADD PARTITION (id = '123');

それでは、SELECT文を実行してみましょう。

SELECT * 
FROM sampledb.table_20200419;

"HIVE_BAD_DATA: Field label's type BINARY in parquet is incompatible with type int defined in table schema"とエラーが表示されました。

Glueのテーブル登録でlabelのデータ型を誤って登録していたことが原因でしょう。

...ということで、table_20200419のスキーマの編集を行いました。

これで先ほどのSELECT文は通るはず....

ダメでした.... 同じエラーメッセージです。(困惑)

Athenaの画面の左側のテーブル情報を見てもわかるのですがlabelのカラムは正常にstring型に更新されております。
それなら、なぜまた同じエラーメッセージなのか??

原因

Glueのテーブル table_20200419へ画面を遷移させ、[パーティションの表示] → [id=123のプロパティの表示]をさせてみましょう。

id=123のパーティションではlabelはint型で扱うように設定されていることがわかります。

パーティションごとのメタデータはそのパーティションを読み込んだ際のテーブル情報が登録されており、大元のテーブルの変更は反映されていないということになります。

対処法

この原因を踏まえて、大元のテーブルのカラム情報が変更された際には今まで読み込んでいたパーティションのメタデータも更新する必要があることがわかりました。

パーティションをDROPした後、改めてパーティションを読み込む対応方法を共有いたします。

ALTER TABLE sampledb.table_20200419 DROP PARTITION (id = '123');
ALTER TABLE sampledb.table_20200419 ADD PARTITION (id = '123');

これで無事、SELECT文が通るようになりました。

この記事がみなさまの開発や分析の一助となれば幸いです。