【データ可視化/Streamlit】StreamlitアプリからファイルをアップロードしてSnowflakeのテーブルを自由に更新する方法

# 前置き
こんにちは。データエンジニアの山口歩夢です！


最近、Streamlitでデータ可視化アプリを作成しており、
Streamlitアプリに行数がやや大きいデータをコピペして、
SnowFlakeのテーブルを自由に更新したい！という状況がありました。

しかし、Streamlitに容量のやや大きいデータを貼り付けると、
ブラウザが重くなったり固まってしまうという問題点が発生してしまいました。

今回はその解決策を考えてみました！
# 結論
データをCSVファイルに貼り付けて、そのCSVファイルと同じ内容にSnowFlakeのテーブルを更新する方法がとても良かったです。

ブラウザが固まることもなくなり、アプリの動きが一気に早くなりました。


# 機能を実装する
早速機能を実装していきます！
## 前準備
まずはStreamlitから更新をするテーブルを用意しておきます。

今回は従業員マスターというテーブルを以下のクエリで作成して、
このテーブルの中にINSERT文などで、いくつか適当にデータを入れておきます。
```sql
create or replace TABLE DEV_YAMAGUCHI_DBT.DATA_CATALOG_LT.EMPLOYEES (
	EMPLOYEE_ID NUMBER(38,0) COMMENT '従業員の氏名。このテーブルの主キー。',
	FIRST_NAME VARCHAR(50) COMMENT '名前。',
	LAST_NAME VARCHAR(50) COMMENT '苗字。',
	SALARY NUMBER(10,2) COMMENT '給与。ドル単位で格納。',
	HIRE_DATE DATE COMMENT '雇用した日。日本時間にて格納。'
)COMMENT='従業員マスター'
;
```
## Streamlitでアプリを作る
テーブルを用意できたので、Streamlitでアプリを作っていきます。
### 1)ディレクトリを用意する
こういった構成でディレクトリを作ります。
```
root/
├── creds.json    # Snowflakeへの接続情報
└── test_app.py   # Streamlitのコード
```
### 2)creds.json
Snowflakeへの接続情報をjson形式で用意しておきます。
```json
{
    "account": "[ACCOUNT名]",
    "user": "[USER名]",
    "password": "[PASSWORD]",
    "warehouse": "[WAREHOUSE名]",
    "database": "[DATABASE名]",
    "schema": "[SCHEMA名]",
    "role": "[ROLE名]"
}
```

## 3)test_app.py
こういったコードを作成しました。
CSVをデータフレーム化して、データフレームの内容でSnowflakeのテーブルを全件洗い替え更新しています。
```test_app.py
import streamlit as st
from snowflake.snowpark import Session
import json
from datetime import time
import pandas as pd


# creds.jsonからSnowflakeの接続情報を取得
def get_config_creds():
    with open('creds.json') as f:
        connection_parameters = json.load(f)
    return connection_parameters

# データセットを取得する
def get_dataset(table):
    df = session.table(table)
    return df


# セッションを作成
connection_parameters = get_config_creds()
st.session_state.snowflake_connection = Session.builder.configs(connection_parameters).create()
session = st.session_state.snowflake_connection

uploaded_file = st.file_uploader("ファイルを選択 or ドラッグしてください。")
employees = get_dataset("EMPLOYEES")

if uploaded_file is not None:
    employees = pd.read_csv(uploaded_file, encoding="utf-8")

if st.button('Submit'):
    try:
        session.write_pandas(
            employees,
            "EMPLOYEES",
            overwrite=True,
            quote_identifiers=False
            )
        st.success("アップロードが成功しました")
        time.sleep(5)
    except Exception as e:
        st.warning(f"アップロードに失敗しました:{e}")
    st.experimental_rerun()
st.dataframe(employees, height=800)

```
以上でアプリの作成が完了です！

# アプリを使ってみる
`$ streamlit run test_app.py`を実行して、実際にアプリを使っていきます。

コマンドを実行するとブラウザ上に以下のように表示されて、
ファイルをアップロードできるようになりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/883573/be8100eb-cb9f-f80c-454e-a6a4671d96d0.png)

試しにCSVにたくさんデータを入れてブラウザにドラッグをしてみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/883573/4d4cec70-401b-bee8-57b5-309524a9495e.png)

ブラウザにドラッグするとCSV内のデータが表示されました。
しっかりCSVの内容を読み込んでくれています！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/883573/9839c78d-5b7e-8aa4-5f38-d9c9f690396c.png)

そしてSubmitを押すと...
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/883573/76f108f8-72f0-ab84-a4ab-d8136dbb0733.png)

CSVに入れておいたデータがSnowflake上のテーブルに入っていました！
しかもブラウザが固まることはなく、スムーズに処理が走りました。


# まとめ
以上でStreamlitからアップロードしたファイルの内容をSnowFlakeに書き込むことができるようになりました。

上記でも申し上げた通り、ただファイルの内容をアップロードしたい時だけでなく、
**Streamlitアプリ上に行数が多いデータをペーストしてSnowflakeを更新したいけどアプリが重くなってしまう**といった時にも役立てることができる機能だなと思いました！

最後に登壇の宣伝をさせていただきます。
3/28(木)の18:30からの以下のイベントで
「StreamlitとTerraformでデータカタログを作った話」をさせていただきます。

ソースコードも公開する予定なので、是非視聴していただけると嬉しいです！

https://datatech-jp.connpass.com/event/310548/

# 参考記事

https://docs.snowflake.com/ja/developer-guide/python-connector/python-connector-api

https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.read_csv.html

https://qiita.com/Lintaro/items/bdf6af79ef7210c8ef2c

https://qiita.com/Ayumu-y/items/0d7c26cad3813327e87d