はじめに
こんにちわ、Mitsuoです。
早速ですが、Amazon Athena(以降Athena) 便利ですよね!?
お手軽にログ分析を始めることが出来る素晴らしいサービスです。
AthenaはAWSサービスのログ分析を行うためのDDLクエリやDMLクエリのサンプルが公式ドキュメントにて用意されており、ほぼSQLの知識が不要で利用が出来ます。
今回はOpenCSVSerDeを用いる際のTIPS、ハマりそうなポイントをご紹介します。
対象読者
Athena初心者の方
OpenCSVSerDeを用いる予定の方、またはOpenCSVSerDeをなんとなくで使っている方
なお、Athenaの基礎基本をまとめたブログを別で書いています。
澤田先輩と優しく学ぶAmazon Athenaです。
良ければ見ていってください。
SerDe、OpenCSVSerDeの概要については、[初学者向け]Amazon AthenaのSerDeを整理してみた(LazySimpleSerDeと OpenCSVSerDe編)を参考にしてください。
TIPS
DDLテーブルで定義出来るデータ型に制限がある
以下データ型に対応しています。
- STRING
- BOOLEAN
- BIGINT
- INT
- DOUBLE
- timestamp
- DATE
timestamp型はUNIXのみ対応
タイムスタンプは1579059880000の様なミリ秒単位の UNIX 数値のみ対応しています。
“YYYY-MM-DD HH:MM:SS.fffffffff” の様なJDBC 準拠の java.sql.Timestampに対応していません。
実際に再現してみる
まずはjava.sql.Timestampのタイムスタンプ型を含むテストデータを用います。
各列項目をダブルクォーテーションでくくっています。
id,name,reservetime "1","澤田","2024-07-01 00:30:00.000" "2","ハヤブサ","2024-07-02 03:00:00.000" "3","みつお","2024-07-05 05:00:00.000" "4","スズケン","2024-07-05 10:00:00.000" "5","りゅうじ","2024-07-05 11:00:00.000" "6","はたはた","2024-07-15 15:00:00.000" "7","大柴","2024-07-15 20:00:00.000" "8,"じゅんた","2024-07-20 16:30:00.000"
DDLクエリを実行します。
WITH SERDEPROPERTIESで引用符を指定していないので、デフォルトで引用符がダブルクォーテーションになっています。
CREATE EXTERNAL TABLE error1_time_csv ( id INT, name STRING, reservetime TIMESTAMP ) ROW FORMAT SERDE 'org.apache.hadoop.hive.serde2.OpenCSVSerde' WITH SERDEPROPERTIES ( "separatorChar" = ",", "escapeChar" = "\\" ) LOCATION 's3のURI' TBLPROPERTIES ("skip.header.line.count"="1");
テーブル作成後、DMLクエリを実行します。
SELECT * FROM error1_time_csv
HIVE_BAD_DATA: Error Parsing a column in the table: For input string: “2024-07-01 00:30:00.000” と言うエラーが表示されます。
エラー分の通り、YYYY-MM-DD HH:MM:SS.fffffffff の形式のログをパースする事が出来ない様です。
次にUNIX形式のタイムスタンプを含むテストデータを用意します。
id,name,reservetime "1","澤田","1721194209599752" "2","ハヤブサ","1721194209599915" "3","みつお","1721194211600532" "4","スズケン","1721194211600715" "5","りゅうじ","1721194211754593" "6","はたはた","1721194211754833" "7","大柴","1721194211754833" "8","じゅんた","1721194212099678"
テストデータを置き直してクエリを実行します。
上述のとおり、reservetime(UNIXTIME)の項目が自動的に YYYY-MM-DD HH:MM:SS.fff の形式に変換されている事を確認しました。
Date型はyyyy/mm/ddの形式で直接指定が出来ない
1970 年 1 月 1 日からの経過日数で指定する必要があります。
例えば2022年7月1日が19174になり、そこから1日ずつインクリメントしたテストデータを用意します。
id,name,date "1","澤田","19174" "2","ハヤブサ","19175" "3","みつお","19176" "4","スズケン","19177" "5","りゅうじ","19178" "6","はたはた","19179" "7","大柴","19180" "8","じゅんた","19181"
DDLクエリを実行します。
CREATE EXTERNAL TABLE date_csv ( id INT, name STRING, date DATE ) ROW FORMAT SERDE 'org.apache.hadoop.hive.serde2.OpenCSVSerde' WITH SERDEPROPERTIES ( "separatorChar" = ",", "escapeChar" = "\\" ) LOCATION 's3のURI' TBLPROPERTIES ("skip.header.line.count"="1");
次にDMLクエリを実行します。
SELECT * FROM date_csv
yyyy/mm/dd形式のテストデータに差し替えた場合はどうでしょうか、確認してみます。
id,name,date "1","澤田","2022-07-01" "2","ハヤブサ","2022-07-02" "3","みつお","2022-07-03" "4","スズケン","2022-07-04" "5","りゅうじ","2022-07-05" "6","はたはた","2022-07-06" "7","大柴","2022-07-07" "8","じゅんた","2022-07-08"
HIVE_BAD_DATA: Error Parsing a column in the table: Cannot convert value 2022-07-01 of type String to a LONG valueと言うエラーを確認しました。パース処理が失敗していることがわかります。
代替手段
出力ログ側の制約で1970 年 1 月 1 日からの経過日数で指定できない場合があるかと思いますが、
その時はSTRING形式で定義した上でSELECT句でDate型に変換する方法があります。
テストデータは同様にyyyy/mm/dd形式の日付を持ったものにします。
id,name,date "1","澤田","2022-07-01" "2","ハヤブサ","2022-07-02" "3","みつお","2022-07-03" "4","スズケン","2022-07-04" "5","りゅうじ","2022-07-05" "6","はたはた","2022-07-06" "7","大柴","2022-07-07" "8","じゅんた","2022-07-08"
DDLクエリを実行します。
dateがDATE型からSTRING型に変わっています。
CREATE EXTERNAL TABLE date_csv ( id INT, name STRING, date STRING ) ROW FORMAT SERDE 'org.apache.hadoop.hive.serde2.OpenCSVSerde' WITH SERDEPROPERTIES ( "separatorChar" = ",", "escapeChar" = "\\" ) LOCATION 's3のURL' TBLPROPERTIES ("skip.header.line.count"="1");
次にDMLクエリを実行します。
SELECT id, name, date_parse(date,'%Y-%m-%d') AS date FROM date_csv
WHERE句でBETWEENが適用されていることがわかります。
まとめ
STRINGのみのCSVファイルであれば気にしなくて良いのですが、DateやTimestamp型を含む場合は考慮が必要になります。
エラー文だけだと理由が分かりにくいとも思うのでTIPSとして持ち帰っていただければ嬉しいです。
また、解説は出来てないのですが数値データ型として定義された列の空値または null 値はSTRING として残すような仕様もあります。
STRINGで定義しておいてSELECT時にCASTで型変換するなどの対処が必要になります。
以上です、Mitsuoでした。
参考資料
AWS公式ドキュメント
OpenCSVSerDe for processing CSV
Why is the TIMESTAMP result empty when I query a table in Amazon Athena?