Streamlit in SnowflakeとGraphvizでオブジェクトの依存関係を表示してみた

こちらの記事は、Snowflake Advent Calender 2023 11日目の記事です。

https://qiita.com/advent-calendar/2023/snowflake

## はじめに

Streamlit in SnowflakeとGraphvizでオブジェクトの依存関係を表示するアプリを作成してみました。

アプリ画面

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3529570/94822505-4d55-07f2-5509-6612fea4a08f.png)

以前公開した記事では、Streamlit in SnowflakeとGraphvizでACCESS_HISTORYビューからリネージュグラフを作成しているので、読んでみてください。

https://qiita.com/kanazawayuto/items/fed3ace9a8c520579cc2

## オブジェクトの依存関係

### 概要

SnowflakeではACCOUNT_USAGEスキーマのOBJECT_DEPENDENCIESビューからオブジェクトの依存関係を参照することができます。

https://docs.snowflake.com/ja/user-guide/object-dependencies

参照可能なオブジェクトは以下のとおりです。

|参照しているオブジェクト|参照されるオブジェクト|
|:----|:----|
|ビュー、セキュアビュー、SQL UDF、SQL UDTF、および名前で参照されるその他のオブジェクト|ビュー、セキュアビュー、マテリアライズドビュー、UDF （すべての種類）、UDTF、および名前で参照されるその他のオブジェクト|
|外部ステージ|ストレージ統合|
|ストリーム|テーブル、ビュー、セキュアビュー|
|外部テーブル|ステージ|
|マテリアライズドビュー|テーブル、外部テーブル|

https://docs.snowflake.com/ja/user-guide/object-dependencies#supported-object-dependencies

### 例

依存関係を表示するビューとタスクを作成

```sql
-- ビューの作成
create or replace table t1 (c1 int);
create or replace table t2 (c1 int);
create or replace view v1 as select * from t1;
create or replace view v2 as select * from t2;
create or replace view v12 as select 1 c1 from v1, v2;

--- タスクの作成
create task t1 as select current_timestamp;
create task t2 after t1 as select current_timestamp;
create task t3 after t1 as select current_timestamp;
create task t4 after t2,t3 as select current_timestamp;
```

OBJECT_DEPENDENCIESビューをクエリ

:::note warn
オブジェクト作成後、最大3時間後にビューが反映される。
:::

```sql
select * from snowflake.account_usage.object_dependencies
order by referencing_object_domain, referencing_database, referencing_schema, referencing_object_name;
```

出力例

|REFERENCED_DATABASE|REFERENCED_SCHEMA|REFERENCED_OBJECT_NAME|REFERENCED_OBJECT_ID|REFERENCED_OBJECT_DOMAIN|REFERENCING_DATABASE|REFERENCING_SCHEMA|REFERENCING_OBJECT_NAME|REFERENCING_OBJECT_ID|REFERENCING_OBJECT_DOMAIN|DEPENDENCY_TYPE|
|:----|:----|:----|:----|:----|:----|:----|:----|:----|:----|:----|
|OBJREFTEST|PUBLIC|T1|1|TASK|OBJREFTEST|PUBLIC|T2|2|TASK|BY_ID|
|OBJREFTEST|PUBLIC|T1|1|TASK|OBJREFTEST|PUBLIC|T3|3|TASK|BY_ID|
|OBJREFTEST|PUBLIC|T3|3|TASK|OBJREFTEST|PUBLIC|T4|4|TASK|BY_ID|
|OBJREFTEST|PUBLIC|T2|2|TASK|OBJREFTEST|PUBLIC|T4|4|TASK|BY_ID|
|OBJREFTEST|PUBLIC|T1|2|TABLE|OBJREFTEST|PUBLIC|V1|1028|VIEW|BY_NAME|
|OBJREFTEST|PUBLIC|V1|1028|VIEW|OBJREFTEST|PUBLIC|V12|4100|VIEW|BY_NAME|
|OBJREFTEST|PUBLIC|V2|3076|VIEW|OBJREFTEST|PUBLIC|V12|4100|VIEW|BY_NAME|
|OBJREFTEST|PUBLIC|T2|1026|TABLE|OBJREFTEST|PUBLIC|V2|3076|VIEW|BY_NAME|


## Graphviz

GraphvizはOSSのグラフ可視化ツールです。dot言語という独自言語で図形を作成できます。

例

![](https://graphviz.org/Gallery/directed/cluster.png)

### Streamlit in Snowflakeでの利用

Streamlit in Snowflakeではstreamlitオブジェクトの `graphviz_chart` 関数にdot言語を直接入力することで、Graphvizの図を作成することができます。Streamlit in SnowflakeではPythonのgraphvizライブラリを使用できない点に注意してください。

https://docs.streamlit.io/library/api-reference/charts/st.graphviz_chart

### 例

dot言語による表記

```dot
digraph {
 "OBJREFTEST.PUBLIC.V1" -> "OBJREFTEST.PUBLIC.T1"
 "OBJREFTEST.PUBLIC.V2" -> "OBJREFTEST.PUBLIC.T2"
 "OBJREFTEST.PUBLIC.V12" -> "OBJREFTEST.PUBLIC.V1"
 "OBJREFTEST.PUBLIC.V12" -> "OBJREFTEST.PUBLIC.V2"
}
```

Graphvizによる画像出力

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3529570/23ae5de8-23f6-9660-72b4-af0f3f914cdd.png)

## アプリ
今回はVIEWとTASKの依存関係を表示するアプリを作成してみました。

### アプリ画面

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3529570/94822505-4d55-07f2-5509-6612fea4a08f.png)


### コード

```python
import json

import pandas as pd

from snowflake.snowpark import Session
from snowflake.snowpark.context import get_active_session
from snowflake.snowpark.exceptions import SnowparkSessionException
import streamlit as st


# Sessionの取得
try:
    # Streamlit in Snowflakeで実行する場合
    session = get_active_session()
except SnowparkSessionException:
    # ローカル環境で実行する場合
    with open('./snowflake_connection_parameters.json') as f:
        connection_parameters = json.load(f)
    session = Session.builder.configs(connection_parameters).create() 

def query_dependencies_parent_df(session, object_domain, start_object):
    # Snowflakeから参照元方向へオブジェクトの依存関係を取得
    query_text = f'''
with od as (
    select
        referenced_object_domain,
        referenced_database ||'.'|| referenced_schema ||'.'|| referenced_object_name as referenced_object_fullname,
        referencing_object_domain,
        referencing_database ||'.'|| referencing_schema ||'.'|| referencing_object_name as referencing_object_fullname
    from
        snowflake.account_usage.object_dependencies
    where
        referenced_object_domain in ('{object_domain}')
        and referencing_object_domain in ('{object_domain}')
)
select
    *
from od
    start with referenced_object_fullname = '{start_object}'
    connect by referenced_object_fullname = prior referencing_object_fullname
;
'''
    return session.sql(query_text).to_pandas()

def query_dependencies_child_df(session, object_domain, start_object):
    # Snowflakeから参照先方向へオブジェクトの依存関係を取得
    query_text = f'''
with od as (
    select
        referenced_object_domain,
        referenced_database ||'.'|| referenced_schema ||'.'|| referenced_object_name as referenced_object_fullname,
        referencing_object_domain,
        referencing_database ||'.'|| referencing_schema ||'.'|| referencing_object_name as referencing_object_fullname
    from
        snowflake.account_usage.object_dependencies
    where
        referenced_object_domain in ('{object_domain}')
        and referencing_object_domain in ('{object_domain}')
)
select
    *
from od
    start with referencing_object_fullname = '{start_object}'
    connect by referencing_object_fullname = prior referenced_object_fullname
;
'''
    return session.sql(query_text).to_pandas()

def genetate_node(from_object_names, to_object_names):
    # Graphvizのノード情報をテキスト出力
    result = ''
    for from_object_name, to_object_name in zip(from_object_names, to_object_names):
        result += f'''"{from_object_name}" -> "{to_object_name}";\n'''
    return result

def generate_graph(graph_parts):
    # Graphvizのグラフを出力
    return f'''digraph {{
{graph_parts}
}}'''

# Streamlitの画面表示
st.title("Object Dependencies Generator")

object_domain = st.selectbox(label='Select Object Domain', options = ['VIEW', 'TASK'])
if object_domain == 'VIEW': object_domain = "VIEW','TABLE"
start_object = st.text_input('Input Object Name', 'DATABASE.SCHEMA.OBJECT')

# Snowflakeからデータの取得
dependencies_child_df = query_dependencies_child_df(session, object_domain, start_object) 
dependencies_parent_df = query_dependencies_parent_df(session, object_domain, start_object)

# Graphvizで表示するためにdot言語の文を生成
graph_parts = ''
graph_parts += genetate_node(dependencies_child_df['REFERENCING_OBJECT_FULLNAME'], dependencies_child_df['REFERENCED_OBJECT_FULLNAME'])
graph_parts += genetate_node(dependencies_parent_df['REFERENCING_OBJECT_FULLNAME'], dependencies_parent_df['REFERENCED_OBJECT_FULLNAME'])
graph = generate_graph(graph_parts)

# Graphvizのグラフを表示
st.graphviz_chart(graph)
```


## 仲間募集

NTTデータ テクノロジーコンサルティング事業本部 では、以下の職種を募集しています。

<details><summary>1. クラウド技術を活用したデータ分析プラットフォームの開発・構築(ITアーキテクト/クラウドエンジニア)</summary>

クラウド／プラットフォーム技術の知見に基づき、DWH、BI、ETL領域におけるソリューション開発を推進します。
https://enterprise-aiiot.nttdata.com/recruitment/career_sp/cloud_engineer

</details>

<details><summary>2. データサイエンス領域（データサイエンティスト／データアナリスト）</summary>

データ活用／情報処理／AI／BI／統計学などの情報科学を活用し、よりデータサイエンスの観点から、データ分析プロジェクトのリーダーとしてお客様のDX／デジタルサクセスを推進します。
https://enterprise-aiiot.nttdata.com/recruitment/career_sp/datascientist

</details>

<details><summary>3.お客様のAI活用の成功を推進するAIサクセスマネージャー</summary>

DataRobotをはじめとしたAIソリューションやサービスを使って、
お客様のAIプロジェクトを成功させ、ビジネス価値を創出するための活動を実施し、
お客様内でのAI活用を拡大、NTTデータが提供するAIソリューションの利用継続を推進していただく人材を募集しています。
https://nttdata.jposting.net/u/job.phtml?job_code=804

</details>

<details><summary>4.DX／デジタルサクセスを推進するデータサイエンティスト《管理職/管理職候補》</summary>
データ分析プロジェクトのリーダとして、正確な課題の把握、適切な評価指標の設定、分析計画策定や適切な分析手法や技術の評価・選定といったデータ活用の具現化、高度化を行い分析結果の見える化・お客様の納得感醸成を行うことで、ビジネス成果・価値を出すアクションへとつなげることができるデータサイエンティスト人材を募集しています。

https://nttdata.jposting.net/u/job.phtml?job_code=898

</details>


## ソリューション紹介

<details><summary> Trusted Data Foundationについて</summary><div>

～データ資産を分析活用するための環境をオールインワンで提供するソリューション～
https://enterprise-aiiot.nttdata.com/tdf/
最新のクラウド技術を採用して弊社が独自に設計したリファレンスアーキテクチャ（Datalake+DWH+AI/BI）を顧客要件に合わせてカスタマイズして提供します。
可視化、機械学習、DeepLearningなどデータ資産を分析活用するための環境がオールインワンで用意されており、これまでとは別次元の量と質のデータを用いてアジリティ高くDX推進を実現できます。

</div></details>


<details><summary> TDFⓇ-AM（Trusted Data Foundation - Analytics Managed Service）について</summary><div>

～データ活用基盤の段階的な拡張支援（Quick Start) と保守運用のマネジメント（Analytics Managed）をご提供することでお客様のDXを成功に導く、データ活用プラットフォームサービス～
https://enterprise-aiiot.nttdata.com/service/tdf/tdf_am
TDFⓇ-AMは、データ活用をQuickに始めることができ、データ活用の成熟度に応じて段階的に環境を拡張します。プラットフォームの保守運用はNTTデータが一括で実施し、お客様は成果創出に専念することが可能です。また、日々最新のテクノロジーをキャッチアップし、常に活用しやすい環境を提供します。なお、ご要望に応じて上流のコンサルティングフェーズからAI/BIなどのデータ活用支援に至るまで、End to Endで課題解決に向けて伴走することも可能です。

</div></details>


<details><summary>NTTデータとTableauについて </summary><div>

ビジュアル分析プラットフォームのTableauと2014年にパートナー契約を締結し、自社の経営ダッシュボード基盤への採用や独自のコンピテンシーセンターの設置などの取り組みを進めてきました。さらに2019年度にはSalesforceとワンストップでのサービスを提供開始するなど、積極的にビジネスを展開しています。

これまでPartner of the Year, Japanを4年連続で受賞しており、2021年にはアジア太平洋地域で最もビジネスに貢献したパートナーとして表彰されました。
また、2020年度からは、Tableauを活用したデータ活用促進のコンサルティングや導入サービスの他、AI活用やデータマネジメント整備など、お客さまの企業全体のデータ活用民主化を成功させるためのノウハウ・方法論を体系化した「デジタルサクセス」プログラムを提供開始しています。
https://enterprise-aiiot.nttdata.com/service/tableau

</div></details>


<details><summary>NTTデータとAlteryxについて </summary><div>
Alteryxは、業務ユーザーからIT部門まで誰でも使えるセルフサービス分析プラットフォームです。

Alteryx導入の豊富な実績を持つNTTデータは、最高位にあたるAlteryx Premiumパートナーとしてお客さまをご支援します。

導入時のプロフェッショナル支援など独自メニューを整備し、特定の業種によらない多くのお客さまに、Alteryxを活用したサービスの強化・拡充を提供します。

https://enterprise-aiiot.nttdata.com/service/alteryx

</div></details>

<details><summary>NTTデータとDataRobotについて </summary><div>
DataRobotは、包括的なAIライフサイクルプラットフォームです。

NTTデータはDataRobot社と戦略的資本業務提携を行い、経験豊富なデータサイエンティストがAI・データ活用を起点にお客様のビジネスにおける価値創出をご支援します。

https://enterprise-aiiot.nttdata.com/service/datarobot

</div></details>

<details><summary> NTTデータとInformaticaについて</summary><div>

データ連携や処理方式を専門領域として10年以上取り組んできたプロ集団であるNTTデータは、データマネジメント領域でグローバルでの高い評価を得ているInformatica社とパートナーシップを結び、サービス強化を推進しています。
https://enterprise-aiiot.nttdata.com/service/informatica

</div></details>

<details><summary>NTTデータとSnowflakeについて </summary><div>
NTTデータでは、Snowflake Inc.とソリューションパートナー契約を締結し、クラウド・データプラットフォーム「Snowflake」の導入・構築、および活用支援を開始しています。

NTTデータではこれまでも、独自ノウハウに基づき、ビッグデータ・AIなど領域に係る市場競争力のあるさまざまなソリューションパートナーとともにエコシステムを形成し、お客さまのビジネス変革を導いてきました。
Snowflakeは、これら先端テクノロジーとのエコシステムの形成に強みがあり、NTTデータはこれらを組み合わせることでお客さまに最適なインテグレーションをご提供いたします。

https://enterprise-aiiot.nttdata.com/service/snowflake

</div></details>