S3 SelectでSQLによるデータ抽出

# S3 Select

[S3 Select](https://docs.aws.amazon.com/AmazonS3/latest/userguide/selecting-content-from-objects.html) は、SQLクエリを用いてS3オブジェクトから必要なデータを抽出する機能です。

大きなオブジェクトから必要なデータのみを抽出してダウンロードすることができ、転送データサイズを縮小化し、遅延を抑えることができます。また、データ抽出、変換をS3にオフロードすることで、アプリケーションでデータ抽出処理を実装する必要がなくなり、処理ロジックをシンプルにできる効果もあります。

![S3 Select概要](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/31469/14df83c0-1bd4-bd87-3e2f-0fdb9e657ab9.png)

※SQLを用いてデータ抽出/分析ができるサービスとして、[Amazon Athena](https://aws.amazon.com/athena/) もあります。S3 Selectは、Athenaと異なり、一つのオブジェクトに対してクエリを発行します、またサポートするSQLもシンプルなSQLサブセットです。大量のオブジェクトを横断して、複雑、大規模なデータ抽出、分析を行うようなユースケースの場合は、Athenaの方が適しています。

## サポートするデータ形式

入力として以下の形式をサポートします。

* CSV
* JSON
* Apache Parquet

以下の圧縮形式を透過的に扱うことができます。
* 無圧縮
* gzip
* bzip2

出力形式は以下が選択できます。
* CSV
* JSON

## 使い方例

入力サンプルとして、仮の機器別の消費電力時系列データを、CSVで用意しました。
カラムは先頭から時刻 (`timestamp`)、機器ID (`device_id`)、消費電力量 (`consumption`) です。

```
timestamp,device_id,consumption
2023-10-01 00:00:00,001,10.5
2023-10-01 00:00:00,002,8.2
2023-10-01 00:00:00,003,12.1
2023-10-01 00:30:00,001,9.8
2023-10-01 00:30:00,002,7.5
2023-10-01 00:30:00,003,11.2
2023-10-01 01:00:00,001,10.2
2023-10-01 01:00:00,002,8.8
2023-10-01 01:00:00,003,12.5
2023-10-01 01:30:00,001,9.3
2023-10-01 01:30:00,002,7.9
2023-10-01 01:30:00,003,11.8
```

S3 Selectを利用するには、[SelectObjectContent](https://docs.aws.amazon.com/AmazonS3/latest/API/API_SelectObjectContent.html) API をコールします。

以下は、上記のCSVオブジェクトに対して、機器ID = '001'、2023-10-01 0時～1時の間の消費電力量のみを抽出して、標準出力に出力するawscliのコマンド例です。

```sh
$ aws s3api select-object-content \
  --bucket DOC-EXAMPLE-BUCKET \
  --key key/of/object \
  --expression "SELECT \"timestamp\", consumption FROM s3object WHERE device_id = '001' and \"timestamp\" BETWEEN '2023-10-01 00:00:00' AND '2023-10-01 00:59:59'" \
  --expression-type SQL \
  --input-serialization '{"CSV": {"FileHeaderInfo": "Use"}}' \
  --output-serialization '{"CSV": {}}' \
  /dev/stdout \

2023-10-01 00:00:00,001,10.5
2023-10-01 00:30:00,001,9.8
```

以下は各必須パラメータの意味です。

```sh
aws s3api select-object-content \
  --bucket バケット名 \
  --key オブジェクトキー  \
  --expression SQL文 \
  --expression-type SQL \ → 現時点で「SQL」固定
  --input-serialization 入力データ仕様 \ → データ形式(CSV, JSON,..) CSV区切り文字、ヘッダ行有無, etc
  --output-serialization 出力データ仕様 \
  出力先ファイルパス
```

S3 Selectで使用されるSQLの正確な仕様は、[AWSドキュメント](https://docs.aws.amazon.com/AmazonS3/latest/userguide/s3-select-sql-reference.html) に記載があります。

[InputSerialization](https://docs.aws.amazon.com/AmazonS3/latest/API/API_InputSerialization.html) (`--input-serialization`) および、[OutputSerialization](https://docs.aws.amazon.com/AmazonS3/latest/API/API_OutputSerialization.html)(`--output-serialization`) は、入出力のデータ仕様を指定します。データ形式の別 (CSV, JSON, Parquet)、圧縮方式、CSVならヘッダ行有無、区切り文字、クオート仕様、JSONならオブジェクト全体で一つのJSONか、それとも行単位に独立したJSONか、等々指定できます。各項目デフォルト値が定義されているのでカスタマイズが必要な個所だけ指定します。個別の詳細は上記の各パラメータにリンクしたAWSドキュメントをご参照ください。

S3マネジメントコンソールからはGUI操作が可能なので、初めて触るときは、使い方のイメージが掴みやすいかもしれません。オブジェクトを選択し、[Object actions] > [Query with S3 Select] を選択してください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/31469/00706b89-25d3-94ea-4f5f-d25557deb425.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/31469/96f8242a-efec-cc00-5680-e52a3116f77d.png)

## 料金

スキャンしたデータサイズ (圧縮ファイルの場合は圧縮状態でのサイズ)、クエリ結果の応答データサイズに対して課金されます。本記事執筆時点で、バージニアの標準ストレージクラスの場合、以下の料金となります:

* スキャンサイズ: 0.002 USD / GB
* 応答サイズ: 0.0007 USD / GB 

## 補足/注意点

* 本記事執筆時点で、[集計関数は一部サポート](https://docs.aws.amazon.com/AmazonS3/latest/userguide/s3-select-sql-reference-aggregate.html)されますが、`GROUP BY` は未サポート。
* CSV出力時にヘッダ行を出力しないようです。
* [予約語](https://docs.aws.amazon.com/AmazonS3/latest/userguide/s3-select-sql-reference-keyword-list.html) と競合する名前のカラムは、ダブルクオートが必要。
    ```sql
    SELECT "timestamp" FROM ...
    ```
* もちろんSSE (サーバーサイド暗号化) も透過的に扱えますが、SSE-KMSを利用している場合はKMSキーへのアクセス権も必要です。