Snowflake Open Catalog における Iceberg テーブルの基本的な操作手順

# 概要

本記事では、Snowflake Open Catalog の全体像を把握するための学習コンテンツを紹介します。Snowflake Open Catalog を検証する際には外部サービスとの連携が必要ですが、ここでは主に Google Colab 上で Spark を利用して操作する手順を解説します。本記事を通じて、Snowflake Open Catalog に関する理解が深まれば幸いです。

- 環境構築
    - Azure Storage 環境の構築
    - Snowflake Open Catalog のアカウントを作成
- Snowflake Open Catalog のカタログ構築
    - 内部カタログの作成
    - Azure Storage に対する認証設定
    - Snowflake Open Catalog におけるロールの作成
    - Snowflake Open Catalog における接続情報の作成
    - Snowflake Open Catalog に対する権限付与
- Spark による Open Catalog のオブジェクト操作
- Iceberg テーブルに対する REFRESH の実行
- Iceberg テーブルに対する 自動 REFRESH 設定の実施

Snowflake Catalog での学習コンテンツについては、以下の記事をご参照ください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/888e19e0-5f3b-4ece-8c53-e1f6195e03a8.png)

出所：[Snowflake Catalog における Iceberg テーブルの基本的な操作手順 #Spark - Qiita](https://qiita.com/manabian/items/69761bde791d0f014246)

本記事は「SnowflakeにおけるApache Iceberg機能の完全ガイド」シリーズの一部です。  
Apache Iceberg機能の全体像やSnowflake上での活用方法について詳しく知りたい方は、以下の記事をご覧ください。

- [SnowflakeにおけるApache Icebergの機能完全ガイド：Apache Iceberg on Snowflake 活用の第一歩 #Spark - Qiita](https://qiita.com/manabian/items/7ba563b614e8ae016cca)

## Snowflake Open Catalog について

Snowflake Open Catalog は、オープンソースのデータカタログ基盤 Apache Polaris をベースにしたマネージドサービスであり、特に Apache Icebergのテーブルカタログとしての役割を担います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/225f7b2e-f37d-42a7-8708-f3324aa6c676.png)

出所：[Snowflakeオープンカタログの概要 | Snowflake Documentation](https://other-docs.snowflake.com/ja/opencatalog/overview)

Snowflake で Apache Iceberg 利用する方法としては、Snowflake をカタログとして利用する方法と、Snowflake Open Catalog（マネージド Apache Polaris）を利用する方法があります。本記事では後者をメインに扱います。Snowflake Open Catalog は、Snowflake のアカウントとは別に Snowflake Open Catalog のアカウントを作成して Iceberg のカタログとして利用できる仕組みです。Snowflake Open Catalog 上で管理しているテーブルは、Snowflake 側からも利用可能です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/786e5d6e-a4b0-4001-a181-7e25312bcf5d.png)

出所：[Apache Iceberg™ テーブル | Snowflake Documentation](https://docs.snowflake.com/ja/user-guide/tables-iceberg)

ドキュメントは、Snowflakeのドキュメントとは別に「Snowflake Open Catalog」の専用タブで表示できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/8128ecfe-373c-4390-b2f1-3f247c720e7f.png)

出所：[Snowflakeオープンカタログの概要 | Snowflake Documentation](https://other-docs.snowflake.com/ja/opencatalog/overview)

本記事は Snowflake の公式チュートリアルをベースとしています。公式チュートリアルもあわせてご確認ください。

- [Getting started with Snowflake Open Catalog | Snowflake Documentation](https://other-docs.snowflake.com/en/opencatalog/tutorials/open-catalog-gs)

クラウドベンダーごとの手順については、以下のドキュメントに記載されています。

- [Azureストレージを使用したカタログの作成 | Snowflake Documentation](https://other-docs.snowflake.com/ja/opencatalog/create-catalog#create-a-catalog-using-azure-storage)
- [Amazon Simple Storage Service (Amazon S3)を使用したカタログの作成 | Snowflake Documentation](https://other-docs.snowflake.com/ja/opencatalog/create-catalog#create-a-catalog-using-amazon-simple-storage-service-amazon-s3)
- [Googleのクラウドストレージを使用したカタログの作成 | Snowflake Documentation](https://other-docs.snowflake.com/ja/opencatalog/create-catalog#create-a-catalog-using-cloud-storage-from-google)

また、Google Colab 上での Spark の基本的な操作方法については、以下の記事をご参照ください。

- [Google Colab 上で Spark を利用する方法 #Python - Qiita](https://qiita.com/manabian/items/7beb6ae0fedf34c2291e)

## 環境構築

### Azure Storage 環境の構築

まず、Azure Storage アカウントを作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/d0405887-f450-4615-826b-3475412b45dd.png)

`snowflake` というコンテナーを作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/aaed88c2-f943-4bc0-8372-4cd5db9b91fe.png)

`opencatalog` というディレクトリを作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/92518cef-88ab-41a1-81f6-e21707d241d7.png)

### Snowflake Open Catalog のアカウントを作成

ORGADMIN ロールに切り替えます。

```sql
-- ORGADMIN にカレントロールを変更
USE ROLE ORGADMIN;
SELECT CURRENT_ROLE();
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/1c89c218-219d-49a2-b4a7-036c55dee1bc.png)

Snowflake Open Catalog が有効な Snowflake アカウントを作成します。

```sql
-- Snowflake Open Catalog が有効な Snowflake アカウントを作成
-- ToDo EMAIL と REEGION をかえる必要あり
CREATE ACCOUNT manabian_opencatalog_00_account
   ADMIN_NAME = 'opencatalog_admin'
   ADMIN_PASSWORD = 'MySecurePassword2025!'
   EMAIL = 'admin@example.com'
   MUST_CHANGE_PASSWORD = FALSE
   EDITION = ENTERPRISE
   REGION = 'AZURE_EASTUS2'
   POLARIS = TRUE;
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/06eac16e-c5e3-4bdb-8844-42ec2cd146e2.png)

Snowflake Open Catalog が有効な Snowflake アカウントの URL を取得します。

```sql
-- カウントの URL を取得
SHOW ACCOUNTS LIKE 'manabian_opencatalog_00_account';
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/57b6f82d-8442-4a9c-a779-8cde911feb82.png)

作成直後にアクセスすると 404 エラーが表示される場合があるため、5 分程度待ちます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/ef82a4e3-a847-40c7-97a4-4452c453f682.png)

ページをリロードし、先ほど設定したアカウントでログインします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/722917b3-a274-4f94-a891-f28aa4848d39.png)

Snowflake Open Catalog のアカウントのウェブインターフェイスに接続できることを確認します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/ed51664b-3a4f-42a6-bb9a-a32a2a1639c2.png)

## Snowflake Open Catalog のカタログ構築

### 内部カタログの作成

#### `Catalogs`タブ -> `+ Catalog`を選択

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/1aeb1b51-9b15-4a33-9b91-c277dfdf53de.png)

#### カタログの設定値を入力後に`Create`を選択

| #    | 項目                            | 設定値例                                                     | 備考                                              |
| ---- | ------------------------------- | ------------------------------------------------------------ | ------------------------------------------------- |
| 1    | Name                            | manabian_catalog                                                             | カタログの名称                                    |
| 2    | External                        | 非チェック                                                   | 外部カタログを利用するかの選択                   |
| 3    | Storage  provider               | Azure                                                        |                                                   |
| 4    | Default  base location          | `wasbs://snowflake@snowflakeicebergqiita.blob.core.windows.net/opencatalog/` | Azure プロトコルではなく、 wasbs または abfss を指定 |
| 5    | Additional locations (optional) | 空文字                                                       |                                                   |
| 6    | Azure tenant ID                 | 095ca098-c723-xxxxxx-xxxxxxxxxx                              | Azure テナント ID                                 |

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/d42fe04e-5eb1-43ee-aff5-e2395b5a90d9.png)

:::note info
本記事は Azure 環境向けの手順を示していますが、Snowflake のドキュメントにはクラウドベンダー別の手順が用意されています。

- [Azureストレージを使用したカタログの作成| Snowflake Documentation](https://other-docs.snowflake.com/ja/opencatalog/create-catalog#create-a-catalog-using-azure-storage)
- [Amazon Simple Storage Service (Amazon S3)を使用したカタログの作成 | Snowflake Documentation](https://other-docs.snowflake.com/ja/opencatalog/create-catalog#create-a-catalog-using-amazon-simple-storage-service-amazon-s3)
- [Googleのクラウドストレージを使用したカタログの作成 | Snowflake Documentation](https://other-docs.snowflake.com/ja/opencatalog/create-catalog#create-a-catalog-using-cloud-storage-from-google)
:::

:::note info
公式ドキュメント上では Blob と ADLS の両方が指定可能となっていますが、ADLS での動作は未検証です。Blob の URI について、公式ドキュメントには「abfss」と記載がありますが、実際には「wasbs」が正しく機能します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/2fc0ee8b-d264-492d-a728-f221896c7f65.png)
:::

出所：[Azureストレージを使用したカタログの作成| Snowflake Documentation](https://other-docs.snowflake.com/ja/opencatalog/create-catalog#create-a-catalog-using-azure-storage)

#### カタログが作成されたことを確認

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/0d23e6a7-e33c-4bdf-a9cd-d54b76312173.png)

### Azure Storage に対する認証設定

#### カタログの設定を表示後、`AZURE_CONSENT_URL` の URL を Azure にログインしているブラウザでアクセス

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/0d23e6a7-e33c-4bdf-a9cd-d54b76312173.png)

#### 外部アプリケーションへのサインイン許可を求めるページが表示されたら、`承諾`を選択

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/f9fd4b38-7201-45ba-9f95-2169356bc4a4.png)

:::note info
この手順は、以下のドキュメントに示されている Microsoft ID プラットフォームでのアクセス許可と同意の設定に相当します。

- [Microsoft ID プラットフォームでのアクセス許可と同意の概要 - Microsoft identity platform | Microsoft Learn](https://learn.microsoft.com/ja-jp/entra/identity-platform/permissions-consent-overview)
:::

#### Azure ポータルで、作成した Azure Storage のページを表示し、`アクセス制御 (IAM)` -> `+ 追加` -> `ロールの割り当ての追加`を選択

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/01d913c8-a7ee-454a-b4c8-e4f5664ba31b.png)

#### `ストレージ BLOB データ共同作成者`を選択して `次へ` をクリック

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/774ee11b-3851-4712-aa47-7224939236d2.png)

#### Snowflake Open Catalog のカタログ設定に表示されている `AZURE_MULTI_TENANT_APP_NAME`（例：`mjonvmsnowflakepacint_1741312865342`）のうち、アンダーバーより前の名称（例：`mjonvmsnowflakepacint`）のアプリケーションを選択し、`選択` -> `レビューと割り当て` をクリック

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/f94f1194-34e7-45de-8ec8-5c12fe76b4e9.png)

Snowflake Open Catalog のカタログ設定に記載されている `AZURE_MULTI_TENANT_APP_NAME` の値を参照します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/0d23e6a7-e33c-4bdf-a9cd-d54b76312173.png)

#### `レビューと割り当て`をクリック

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/beb9ed28-ff62-4849-8169-8fd3b6dedd84.png)

#### `Role assignments` タブで割り当てが追加されていることを確認

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/601c3b0e-74a1-4b01-8bc9-66430acac711.png)

### Snowflake Open Catalog におけるロールの作成

#### Snowflake Open Catalog で `Connections` タブ -> `Roles` タブ -> `+ Principal Role` を選択

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/88731a3f-f8fb-4a39-a6b7-9b86c444816f.png)

#### `Name` に `spark_role` と入力し、`Create` をクリック

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/b1b41ea9-be26-49ec-b0ae-2d1f96a5f251.png)

### Snowflake Open Catalog における接続情報の作成

#### Snowflake Open Catalog で `Connections` タブ -> `Principal` タブ -> `+ Connection` を選択

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/27088a43-4f13-4672-8aaf-244c796f9bb3.png)

#### 項目に値を設定して `Create` をクリック

| #    | 項目            | 設定値例      |
| ---- | --------------- | ------------- |
| 1    | Name            | google_colab  |
| 2    | Query Engine    | Apache Spark  |
| 3    | Principal  Role | spark_role    |

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/ff14a749-734b-4f81-8ee5-cd9a95f14c66.png)

#### Spark からアクセスする際に利用する `Client ID` と `Client Secret` を取得

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/8938c66a-a6ad-4852-9ff3-478a85e67372.png)

### Snowflake Open Catalog に対する権限付与

#### `Catalogs` -> 作成済みのカタログ -> `Roles` -> `+ Catalog Role` を選択

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/21a65fbf-2112-4e05-b9c5-0e16b9571c38.png)

#### 項目に値を設定して `Create` をクリック

| #    | 項目       | 設定値例               |
| ---- | ---------- | ---------------------- |
| 1    | Name       | spark_catalog_role     |
| 2    | Privileges | CATALOG_MANAGE_CONTENT |

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/ae8b20a8-ac06-4935-8b0c-ad0091ca90b3.png)

#### `Grant to Principal Role` を選択

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/235aa918-66fa-4366-b0ef-ec08e3631d7f.png)

#### 項目を設定して `Grant` をクリック

| #    | 項目                  | 設定値例           |
| ---- | --------------------- | ------------------ |
| 1    | Catalog role to grant | spark_catalog_role |
| 2    | Catalog role to grant | spark_role         |

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/67657525-a68c-43a9-bfae-fea481cdef78.png)


## Spark による Open Catalog のオブジェクト操作

### Azure Storage で `アクセスキー` タブにある `key1` の値を取得

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/69485420-e8e3-403c-9d56-ff8753dbc9d6.png)

### Google Colab のノートブックを作成

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/0951e838-aa88-4fa0-8bc6-0087e03db4fb.png)

### Spark のバージョンを確認

```bash
!pyspark --version
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/1a7f8979-76f8-46ff-9139-fe265c6ca7d4.png)

### Snowflake Open Catalog の account_identifier を変数にセット

```py
open_catalog_account_identifier = "mtirxwm-manabian_opencatalog_00_account"
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/bff0dc73-5566-4fcf-919e-feb7c023c28b.png)

account_identifier は管理画面の URL から確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/aaddcb93-59eb-495e-8abb-fd34b8db1335.png)

### カタログ名と認証情報を変数にセット

```py
catalog_name = "manabian_catalog"
client_id = "OmD7CNsfKOeqt8jAe4QXXXXXX="
client_secret = "hCfWMmj+b4T5DJvI/uyxXXXXXXX="
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/3fcaaa0b-d670-4d7f-bfca-e0f1c13a1351.png)

### Azure Storage の名称とキーを変数にセット

```py
# Azure Storage に関する情報をセット
azure_storage_account_name = "snowflakeicebergqiita"
azure_storage_account_key = "cBGfLSEInUgJ+e6w/bx7x4yewwOqDIIGE7sjPUMttki9CzXXXXXXXX=="
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/b4228b31-d138-4c10-a254-188c58d2a806.png)

### SparkSession の定義と疎通確認

```python
from pyspark.sql import SparkSession

spark = (
    SparkSession.builder.appName("iceberg_lab")
    .config(
        "spark.jars.packages",
        'org.apache.iceberg:iceberg-spark-runtime-3.5_2.12:1.4.1,'
        'org.apache.iceberg:iceberg-azure-bundle:1.5.2,'
        'org.apache.hadoop:hadoop-azure:3.4.1'
    )
    .config('spark.sql.extensions', 'org.apache.iceberg.spark.extensions.IcebergSparkSessionExtensions')
    .config('spark.sql.defaultCatalog', 'opencatalog')
    .config('spark.sql.catalog.opencatalog', 'org.apache.iceberg.spark.SparkCatalog')
    .config('spark.sql.catalog.opencatalog.type', 'rest')
    .config('spark.sql.catalog.opencatalog.header.X-Iceberg-Access-Delegation','vended-credentials')
    .config('spark.sql.catalog.opencatalog.uri',f'https://{open_catalog_account_identifier}.snowflakecomputing.com/polaris/api/catalog')
    .config('spark.sql.catalog.opencatalog.credential',f'{client_id}:{client_secret}')
    .config('spark.sql.catalog.opencatalog.warehouse',catalog_name)
    .config('spark.sql.catalog.opencatalog.scope',f'PRINCIPAL_ROLE:ALL')
    .config(f'spark.hadoop.fs.azure.account.key.{azure_storage_account_name}.blob.core.windows.net', azure_storage_account_key)
    .getOrCreate()
)
spark
```

```python
spark.sql("show namespaces").show()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/134c97d5-410c-4a9b-86c7-8874f046a53f.png)

### Namespace を作成

```py
spark.sql("create namespace if not exists spark_demo")
spark.sql("show namespaces").show()
spark.sql("use namespace spark_demo")
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/7b87a715-4d27-4b1a-a7b4-141e5260ec7a.png)

Snowflake Open Catalog のウェブインターフェースにて、Namespace が作成されていることを確認します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/a3194b95-2bf1-4f4d-b364-bcf25fc93bb0.png)

### テーブルを作成

```py
spark.sql("create or replace table test_table (col1 int) using iceberg");
spark.sql("show tables").show()
```

Snowflake Open Catalog のウェブインターフェースにて、テーブルが作成されていることを確認します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/743163e0-18e9-48e8-84a6-60bf4be813ee.png)

### 

```py
spark.sql("insert into test_table values (1)");
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/922b7521-75eb-4865-8b17-d09efe372407.png)

:::note info
もしこの手順でエラーになる場合は、以下の記事を参考にしてください。

- [Azure Storage で構築した Snowflake Open Catalog の外部テーブルに INSERT する際のエラーに対する暫定対応方法 #Spark - Qiita](https://qiita.com/manabian/items/5dee394656b575f78801)
:::

### 

```py
spark.table("test_table").show();
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/63b04b88-4952-4d15-9286-c56218b238ee.png)


## Snowflake アカウントへのテーブルの手動同期

### Snowflake Open Catalog のカタログ統合を作成

```sql
-- 各値を変数としてセット
SET open_catalog_account_identifier = 'mtirxwm-manabian_opencatalog_00_account';
SET catalog_name = 'manabian_catalog';
SET namespace_name = 'spark_demo';
SET client_id = 'OmD7CNsfKOeqt8jAe4QXXXXXX=';
SET client_secret = 'hCfWMmj+b4T5DJvI/uyxXXXXXXX=';

-- 連結した URL は変数に事前に設定
SET full_catalog_uri = 'https://' || $open_catalog_account_identifier || '.snowflakecomputing.com/polaris/api/catalog';


-- 変数を利用してカタログ統合を作成
CREATE OR REPLACE CATALOG INTEGRATION demo_open_catalog_int 
    CATALOG_SOURCE = POLARIS 
    TABLE_FORMAT = ICEBERG 
    CATALOG_NAMESPACE = $namespace_name
    REST_CONFIG = (
        CATALOG_URI = $full_catalog_uri
        WAREHOUSE = $catalog_name
    )
    REST_AUTHENTICATION = (
        TYPE = OAUTH 
        OAUTH_CLIENT_ID = $client_id 
        OAUTH_CLIENT_SECRET = $client_secret 
        OAUTH_ALLOWED_SCOPES = ('PRINCIPAL_ROLE:ALL')
    ) 
    ENABLED = true;
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/ad6b6792-bd86-439f-93a9-e86c04c748a1.png)

作成されたら確認します。

```sql
DESC CATALOG INTEGRATION demo_open_catalog_int;
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/8f5333a2-3459-4a8b-bd27-36f215d64071.png)

### 外部 Volume を作成

CREATE EXTERNAL VOLUME 文に変数を指定できないため、設定値を生成してコピー＆ペーストする方法をとります。まずは必要な値を生成します。

```sql
-- 各値を変数としてセット
SET azure_tenant_id = '095ca098-c723-478f-xxxx-xxxxx';
SET azure_storage_account_name = 'snowflakeicebergqiita';
SET storage_provider = 'AZURE';

-- 連結した URL は変数に事前に設定
SET full_storage_uri = 'azure://' || $azure_storage_account_name || '.blob.core.windows.net/snowflake/opencatalog/';


SELECT
    $storage_provider AS  STORAGE_PROVIDER
    ,$full_storage_uri AS STORAGE_BASE_URL
    ,$azure_tenant_id AS AZURE_TENANT_ID;
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/155b2625-0dad-4b00-8233-cd30dc6ef0bd.png)

上記で取得した値を参考にしながら、以下の CREATE EXTERNAL VOLUME 文をコピー＆ペーストして実行します。

```sql
-- 外部 Volume を作成
CREATE OR REPLACE EXTERNAL VOLUME my_azure_volume
    STORAGE_LOCATIONS = (
        (
          NAME = 'my-azure-container'
          STORAGE_PROVIDER = 'AZURE'
          STORAGE_BASE_URL = 'azure://snowflakeicebergqiita.blob.core.windows.net/snowflake/opencatalog/'
          AZURE_TENANT_ID = '095ca098-c723-478f-xxxx-xxxxx'
        )
    )
    ALLOW_WRITES = false;
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/38c0089d-45b3-4252-91d3-d00fd1a6426d.png)

作成を確認します。

```sql
DESC EXTERNAL VOLUME my_azure_volume;
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/030095cb-50a2-4f3e-9a03-636814695634.png)

### 外部 Volume に対する権限の付与

AZURE_CONSENT_URL の URL を Azure にログインしているブラウザでアクセスします。

```sql
DESC EXTERNAL VOLUME my_azure_volume;
SELECT 
    PARSE_JSON($4):AZURE_MULTI_TENANT_APP_NAME::string AS AZURE_MULTI_TENANT_APP_NAME,
    PARSE_JSON($4):AZURE_CONSENT_URL::string AS AZURE_CONSENT_URL
FROM TABLE(RESULT_SCAN(LAST_QUERY_ID()))
WHERE $2 = 'STORAGE_LOCATION_1';
```

`承諾`を選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/0ac04b60-8a5b-47c8-b66c-a3a24be08125.png)

Azure Storage 側で、`AZURE_MULTI_TENANT_APP_NAME` のアンダーバーより前のアプリケーション（例：`ps27tlsnowflakepacint`）に対して権限を付与します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/d31403fe-9c9d-4067-81cd-887d17b1dacb.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/4f6f351c-726f-43e9-a4e7-0a34b31dcc2a.png)

### 検証用のデータベースを作成

```sql
CREATE OR REPLACE DATABASE OPEN_CATALOG_TUTORIAL_DB;
USE DATABASE OPEN_CATALOG_TUTORIAL_DB;
```

### 5 分程度待機してから Iceberg テーブルを作成

```sql
create or replace iceberg table test_table
    catalog = 'demo_open_catalog_int'
    external_volume = 'my_azure_volume'
    catalog_table_name = 'test_table';
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/fc8b7680-33b7-48fc-a4b1-10149eaf9629.png)

### Iceberg テーブルに対するクエリを実行

```sql
SELECT
    *
FROM
    test_table;
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/d79aca75-2f63-4a21-8d13-7cf1ae9efe03.png)

### Iceberg テーブルに対する REFRESH の実行

Spark（Google Colab）から書き込みを実行します。

```python
spark.sql("use namespace spark_demo")
spark.sql("insert into test_table values (2)");
spark.table("test_table").show();
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/04a05ff5-1ce9-4e89-b812-9587fec6e6e6.png)

Snowflake 側ではすぐには新しいデータが反映されません。これはメタデータの情報がまだ同期されていないためです。

```sql
SELECT
    *
FROM
    test_table;
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/10603ec3-b290-4ceb-80bd-f349fd65a233.png)

テーブルに対して REFRESH を実行し、データが更新されることを確認します。

```sql
ALTER ICEBERG TABLE test_table REFRESH;
SELECT
    *
FROM
    test_table;
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/9b95e075-c784-4a27-b13e-75f476e7faa6.png)

### Iceberg テーブルに対する 自動 REFRESH 設定の実施

Iceberg テーブルの `AUTO_REFRESH` オプションを `TRUE` に設定します。

```sql
ALTER ICEBERG TABLE test_table SET
    AUTO_REFRESH = TRUE;
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/a030593a-6d7d-4848-aacf-4e171f048a8b.png)

Spark（Google Colab）からデータの書き込みを再度実行します。

```python
spark.sql("use namespace spark_demo")
spark.sql("insert into test_table values (3)");
spark.table("test_table").show();
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/1666b11e-1d26-4a86-b90d-aa0c43359e58.png)

Snowflake 側でクエリを実行すると、自動で 3 のレコードが反映されていることを確認できます。

```sql
SELECT
    *
FROM
    test_table;
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/15638ea5-e679-4773-b944-14fe6a62bc3e.png)
```