AthenaでのWAFログ解析：ARRAYとSTRUCTの扱い方



AWS WAF のログを Amazon Athena で分析するため、公式ドキュメントを参考にテーブル定義を作成しました。
本記事ではその定義と、Athena 特有のネスト構造データ（ARRAY や STRUCT）の扱いについて解説します。

https://docs.aws.amazon.com/ja_jp/athena/latest/ug/create-waf-table-partition-projection.html

## テーブル定義

```
CREATE EXTERNAL TABLE `waf_logs`(
  `timestamp` bigint,
  `formatversion` int,
  `webaclid` string,
  `terminatingruleid` string,
  `terminatingruletype` string,
  `action` string,
  `terminatingrulematchdetails` array <
                                    struct <
                                        conditiontype: string,
                                        sensitivitylevel: string,
                                        location: string,
                                        matcheddata: array < string >
                                          >
                                     >,
  `httpsourcename` string,
  `httpsourceid` string,
  `rulegrouplist` array <
                      struct <
                          rulegroupid: string,
                          terminatingrule: struct <
                                              ruleid: string,
                                              action: string,
                                              rulematchdetails: array <
                                                                   struct <
                                                                       conditiontype: string,
                                                                       sensitivitylevel: string,
                                                                       location: string,
                                                                       matcheddata: array < string >
                                                                          >
                                                                    >
                                                >,
                          nonterminatingmatchingrules: array <
                                                              struct <
                                                                  ruleid: string,
                                                                  action: string,
                                                                  overriddenaction: string,
                                                                  rulematchdetails: array <
                                                                                       struct <
                                                                                           conditiontype: string,
                                                                                           sensitivitylevel: string,
                                                                                           location: string,
                                                                                           matcheddata: array < string >
                                                                                              >
                                                                   >,
                                                                  challengeresponse: struct <
                                                                            responsecode: string,
                                                                            solvetimestamp: string
                                                                              >,
                                                                  captcharesponse: struct <
                                                                            responsecode: string,
                                                                            solvetimestamp: string
                                                                              >
                                                                    >
                                                             >,
                          excludedrules: string
                            >
                       >,
`ratebasedrulelist` array <
                         struct <
                             ratebasedruleid: string,
                             limitkey: string,
                             maxrateallowed: int
                               >
                          >,
  `nonterminatingmatchingrules` array <
                                    struct <
                                        ruleid: string,
                                        action: string,
                                        rulematchdetails: array <
                                                             struct <
                                                                 conditiontype: string,
                                                                 sensitivitylevel: string,
                                                                 location: string,
                                                                 matcheddata: array < string >
                                                                    >
                                                             >,
                                        challengeresponse: struct <
                                                            responsecode: string,
                                                            solvetimestamp: string
                                                             >,
                                        captcharesponse: struct <
                                                            responsecode: string,
                                                            solvetimestamp: string
                                                             >
                                          >
                                     >,
  `requestheadersinserted` array <
                                struct <
                                    name: string,
                                    value: string
                                      >
                                 >,
  `responsecodesent` string,
  `httprequest` struct <
                    clientip: string,
                    country: string,
                    headers: array <
                                struct <
                                    name: string,
                                    value: string
                                      >
                                 >,
                    uri: string,
                    args: string,
                    httpversion: string,
                    httpmethod: string,
                    requestid: string
                      >,
  `labels` array <
               struct <
                   name: string
                     >
                >,
  `captcharesponse` struct <
                        responsecode: string,
                        solvetimestamp: string,
                        failureReason: string
                          >,
  `challengeresponse` struct <
                        responsecode: string,
                        solvetimestamp: string,
                        failureReason: string
                        >,
  `ja3Fingerprint` string,
  `oversizefields` string,
  `requestbodysize` int,
  `requestbodysizeinspectedbywaf` int
)
```




## atenaとRDBとの違い
MySQLなどの一般的なRDBでは、1つのカラムに複数の値を持たせることは推奨されておらず、代わりに別テーブルにして正規化します（例：1対多の関係）。
AthenaやBigQueryのような分散SQLエンジンでは、配列をそのまま持てるようです。
RDBに慣れているとデータの扱いが違うところがあり戸惑うかもしれません。



## ARRAY（配列）
ARRAY は、同じデータ型の値を複数まとめて1つのカラムに格納できるデータ型です。
イメージとしては「SQL版のリスト」であり、例えば ["red", "blue", "green"] のように、複数の文字列を1つのフィールドに入れることができます。

```
["red", "blue", "green"]
```

## STRUCT（構造体）
STRUCT、異なる型の複数のフィールドを1つのカラムにまとめたものです。以下の様にSTRING,INT型など異なる方を一つのカラムにまとめることができます。ドット記法で対象のデータにアクセス可能です。

```
STRUCT<name STRING, age INT>
```

## UNNEST （配列やマップの展開）
UNNEST は、配列やマップの中身を1つずつ行に展開するための関数です。
JOIN句とセットで使用します。配列やマップを展開して、元の行と結合します。

```
SELECT color
FROM your_table
CROSS JOIN UNNEST(colors) AS u (color);
```

https://docs.aws.amazon.com/ja_jp/athena/latest/ug/flattening-arrays.html

 ## まとめ

 MySQLなどのリレーショナルデータベースでは、カラムは1値（スカラー型）を持つのが前提で、配列や構造体のような「ネスト構造」は扱いません。
しかし、Athenaでは ARRAY や STRUCT を活用することで、JSON形式のような複雑なデータをそのまま処理できます。
特に UNNEST 関数を使えば、配列を1行ずつ展開し、元の行と結合することが可能で、JOIN句と組み合わせて使うのが基本です。

https://docs.aws.amazon.com/ja_jp/athena/latest/ug/ddl-sql-reference.html