【データ基盤構築/Snowflake】外部ステージを参照しているビューをマテリアライズドビュー化してコスト削減する

# 前置き
こんにちは。データエンジニアの山口歩夢です！
この度、技術書典17に向けて、Streamlitの技術書を執筆しました。

技術書典16で頒布した『Streamlit データ可視化入門』に引き続き、今回はさらに詳しくStreamlitの全貌を解説し、実践的なアプリケーション開発手法を豊富なユースケースとともに紹介しています。
また、SnowflakeのLLMを活用したChatBotの開発にも触れ、最新技術を取り入れたアプリケーション開発についても紹介しました。

https://techbookfest.org/product/fhiwFCAW3weeFibUeFc4p1


# 本題
それでは本題です！
外部ステージを参照しているビューを外部テーブルやマテリアライズドビューに置き換えることで、金額やクエリなどの面でパフォーマンスを向上させられる場合があります。今回は、そのお話をしようと思います。


# マテリアライズドビューとは
マテリアライズドビューとは、データの実体を持ったビューのことです。
定期的にマテリアライズドビュー内で保持するデータを更新して、最新のデータを保持してくれます。

一方、通常のビューはデータの実体を持っておらず、呼び出すと毎回ビューに設定してあるSQLを実行してデータを抽出してくれるものです。

外部ステージに対する直接のクエリ実行は、Snowflake内のテーブルに対して実行するクエリと比べて、パフォーマンスが良く無い場合があります。
つまり、外部ステージを参照しているビューに対してクエリを頻繁に実行すると、パフォーマンスが良くないクエリを頻繁に実行してしまうことになってしまいます。

そこで、外部ステージを参照した外部テーブルを作成し、さらに外部テーブルを参照したマテリアライズドビューを作成し、マテリアライズドビューに対してクエリすることでパフォーマンスを向上させることができます。

https://docs.snowflake.com/ja/user-guide/views-materialized

https://docs.snowflake.com/ja/sql-reference/sql/create-materialized-view


# マテリアライズドビューを使わない場合
外部ステージであるS3にファイルを入れ、
その外部ステージを参照しするかたちでVIEWを作成し、Snowflake上でデータを参照することが可能になります。

![マテリアライズドビュー.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/883573/7452cb1f-0cdb-29c9-9b68-7b923e33ab02.png)

実装は簡単ですが、Snowflakeから毎回外部ステージを参照しにいく必要があるため、
クエリのパフォーマンスや金額面でのパフォーマンスが悪くなる場合があります。

# マテリアライズドビューを使う場合
S3の外部ステージから外部テーブルを作成し、外部テーブルを参照するマテリアライズドビューを作成します。
つまり、外部ステージのデータにはマテリアライズドビュー経由でアクセスするかたちになっています。
外部ステージにあるデータが半構造化データであれば、マテリアライズドビュー作成時(CREATE MATERIALIZED VIEW文の中で)構造化しておくと便利です。

![マテリアライズドビュー.drawio (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/883573/6fecb3e3-dc63-be61-93ba-94fa4f81a8d7.png)

こちらの方法で実装すれば、金額・クエリ実行面でパフォーマンスの向上が見込めます。
**外部ステージからは直接マテリアライズドビューを作成することはできないので、**
**外部ステージとマテリアライズドビューの間に外部テーブルを間に挟む必要があります。**

# 実装手順

## 1)S3バケットを作成
S3のコンソール画面を開きます。
そして、「バケットを作成」をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/883573/f2098b45-5236-0122-0656-eecc6c6606af.png)

バケットを作成画面に遷移したら、バケット名を指定して「バケットを作成」をクリックしてバケットを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/883573/f1b0b19d-a8a8-8666-7539-d0f45bd4e8fb.png)

## 2)外部ステージを作成
Snowflake上で、上記のバケットを参照する外部ステージの作成を行います。
上記バケットを参照する外部ステージを作成します。
以下の記事を参考に外部ステージを作成してください。非常に分かりやすく解説してくださっております！

https://qiita.com/KimiyukiMuramatsu/items/56dbb35f48b69b7b36a8

## 3)外部テーブルを作成
作成した外部ステージを参照する外部テーブルの作成を行います。

以下のようなクエリで外部テーブルを作成します。
`AUTO_REFRESH = TRUE`と指定することによって、外部ステージのファイルが更新される度に、外部テーブルのデータが更新されるように実装できます。これで外部テーブルに外部ステージの最新のデータを反映することができます。

```sql
CREATE OR REPLACE EXTERNAL TABLE [外部ステージ名]
WITH LOCATION = @external_stage -- 作成した外部ステージを指定
FILE_FORMAT = (TYPE = PARQUET)  -- ファイルフォーマットを指定
AUTO_REFRESH = TRUE             -- 自動更新をON
;
```

https://docs.snowflake.com/ja/user-guide/tables-external-intro

## 4)S3バケットのイベント通知に外部テーブルのnotification channelを設定
>`AUTO_REFRESH = TRUE`と指定することによって、外部ステージのファイルが更新される度に、外部テーブルのデータが更新されるように実装できます。これで外部テーブルに外部ステージの最新のデータを反映することができます。

先ほど上記のように書きましたが、あと一つ設定が必要です。
S3バケットのイベント通知に、外部テーブルの`Notification Channel`のSQSのarnを設定しましょう。
以下のクエリを実行すると外部テーブルの情報を抽出することができます。

```sql
show external tables;
```

そのうち、`notification_channel`というカラムには、AWSのSQSのarnが含まれています。こちらを、外部ステージとして使用しているS3のイベント通知に設定します。
S3のコンソール画面から「プロパティ」をクリックすると、以下のような「イベント通知」の項目があるページに遷移できるので、以下のように設定をします。
以下の画像は「parquet」ファイルがS3に配置されると、外部テーブルに反映されるように設定されています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/883573/13cc5102-1081-3376-815d-2c5e424b8114.png)





<font color="red">外部テーブルの自動更新のイベント通知にはSnowpipeが使用されるようです！</font color="red>
S3イベント通知がSQSを経由して、Snowpipeに新しいファイルがS3にロードされたことを通知し、新しいファイルのデータがSnowflakeの外部テーブルに反映されるイメージです。

## 5)マテリアライズドビューを作成
以下のようなクエリで、外部テーブルからマテリアライズドビューを作成します。
外部ステージのデータが半構造化データであれば、この段階で構造化すると便利でした！

```sql
CREATE OR REPLACE MATERIALIZED VIEW [マテリアライズドビューの名前]
AS 
SELECT
    $1:col1 AS col1,
    $1:col2 AS col2,
    $1:col3 AS col3
FROM
    [外部テーブルの名前]
;
```

以上で外部ステージのデータを、Snowflake上でマテリアライズドビューとして使用する準備が完了しました！

# まとめ
以下が今回の学びでした！
マテリアライズドビューの使用タイミングの理解を深めることができて良かったと思います。
* 外部ステージを直接クエリするのは、クエリや金額面でパフォーマンスが良くない場合がある
  * 外部テーブルを作って、外部テーブル経由で参照することで対処するのが良さそう
* 外部テーブルに外部ステージの最新の状態を反映するには、S3のイベント通知に外部テーブルのnotification channelの設定を行う必要がある
* 外部ステージのデータが半構造化データの場合、外部ステージからマテリアライズドビューを作成して、マテリアライズドビュー作成の段階で構造化するのが良さそう


https://docs.snowflake.com/ja/user-guide/views-materialized