この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
問題
S3 Selectで、以下のようにCSVの列名に特殊な文字が入っている場合の話です。
aaa/aaa,bbb?bbb,ccc;ccc
aaa1,bbb1,ccc1
aaa2,bbb2,ccc2
ここで例えばaaa/aaa
列だけを取り出したいとき、どうすれば良いでしょうか?
とりあえずやってみる
Python (boto3)でやってみます。まず、以下のようなコードを書いてみます。FileHeaderInfo: USE
としてヘッダ行を使用する設定にしています。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import boto3
client = boto3.client('s3', 'ap-northeast-1')
bucket_name = 'test-bucket-name'
key = 'test.csv'
response = client.select_object_content(
Bucket = bucket_name,
Key = key,
ExpressionType = 'SQL',
Expression = 'SELECT s.aaa/aaa FROM S3Object s',
InputSerialization = {
'CompressionType': 'NONE',
'CSV': {
'FileHeaderInfo': 'USE',
'RecordDelimiter': '\n',
'FieldDelimiter': ','
}
},
OutputSerialization = {
'CSV': {
'RecordDelimiter': '\n',
'FieldDelimiter': ','
}
}
)
for event in response[ 'Payload' ]:
if 'Records' in event:
records = event[ 'Records' ][ 'Payload' ].decode( 'utf-8' )
print(records)
これを実行すると、以下のようなエラーになります。
botocore.exceptions.ClientError: An error occurred (MissingHeaders) when calling the SelectObjectContent operation: Some headers in the query are missing from the file. Please check the file and try again.
では、どうすれば良いのでしょうか?
解決
前置きが長すぎましたが、列名aaa/aaa
をダブルクォートでくくれば解決します。分かっていれば簡単ですね。
Expression = 'SELECT s."aaa/aaa" FROM S3Object s',
実行すると以下のように正しく結果が得られます。
aaa1
aaa2
なお、S3 Selectにおいては、以下のようにダブルクォートには特別な意味があります。
- ダブルクォートでくくると、大文字・小文字が区別されるようになります ヘッダー/属性名の大文字と小文字の区別
- 予約語が列名になっているような場合もダブルクォートでくくれば使えます ユーザー定義の用語としての予約キーワードの使用
補足:スラッシュを含むCSV
そもそも列名にそんな変な文字入れなきゃいいじゃん、という話なのですが、実は列名にスラッシュが含まれているCSVが存在します。 それがAWSのコストと使用状況レポート(AWS Cost & Usage Report, 以下CUR)です。
CURのヘッダ行にはlineItem/ProductCode
, lineItem/UsageType
のようにスラッシュが含まれています。
ですので、これをS3 Selectで扱う場合はダブルクォートが必要です。例えばCURからS3関係の行だけを取り出したければ以下のようになります。
SELECT * FROM S3Object s WHERE s."lineItem/ProductCode" = 'AmazonS3'
業務上でCURを扱う機会があり、S3 Selectを使ってみたらちょっとハマったのでメモとして残してみました。レアケースだとは思いますが、どこかで誰かのお役に立てば幸いです。