[小ネタ] S3 SelectでCSVの列名に特殊文字が入っている場合

問題

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を使ってみたらちょっとハマったのでメモとして残してみました。レアケースだとは思いますが、どこかで誰かのお役に立てば幸いです。