【Snowflake】Snowflake ~ Partition構成のデータを取り込む ~

■ はじめに

 パーティション (Partition) のようなパス構成
(e.g. s3://your-s3-bucket/test/demo/date=20221028/)のデータを
Snowflake で取り込む必要がでてきそうだったので
その方法についてまとめる。

後日談

* 色々調べてみると、同じようなことを公式ドキュメントでもやってた
 => 一旦、以下の公式ドキュメントを確認した方がいいかも。

https://docs.snowflake.com/ja/sql-reference/sql/create-external-table.html#examples

目次

【1】今回のお題
 1)フォルダ構成例
 2)補足:パーティションについて
【2】対処案
 1)SQL例
【3】使用した技術事項
 1)メタデータ「METADATA$FILENAME」
 2)SPLIT_PART()

【1】今回のお題

* 以下「フォルダ構成例」のように、
 パーティションのような「city=<city_name>」が
 入ったパス構成のものを一回のSQLで吸い出して、
 パーティションの値(今回の場合、<city_name>)も含めて
 Snowflakeのテーブルに入れたい。

1)フォルダ構成例

s3://your-s3-bucket/test/demo/
 + city=tokyo/
    + xxxx1.json.gz
    + xxxx2.json.gz
 + city=osaka/
    + yyyy1.json.gz
    + yyyy2.json.gz
 + city=fukuoka/
    + zzzz1.json.gz
    + zzzz2.json.gz

2)補足:パーティションについて

https://dk521123.hatenablog.com/entry/2020/07/16/224332

でやったように
Spark(Hive)の PARTITIONED BY が Snowflake にあればいいのだが
Snowflakeのステージには、パーティションという概念がそもそもない
 => 以下の公式ドキュメント参照。

https://docs.snowflake.com/ja/sql-reference/sql/create-stage.html

補足:CREATE EXTERNAL TABLE

* CREATE EXTERNAL TABLE なら「PARTITION BY」はある。

https://docs.snowflake.com/ja/sql-reference/sql/create-external-table.html

【2】対処案

* 対処のポイントとしては以下の通り。
~~~~~
[1] ステージ作成時のURLの指定は、
 パーティション前のパスを指定
 => 指定した配下のファイル全て(ファルダ内でも)
  取得してくれる。

[2] パーティション部分は、
 METADATA$FILENAMEおよび文字列操作関数
 (今回の場合、SPLIT_PART関数)を使って
 切り出して取得する
~~~~~
 => Snowflake の 文字列操作関数 については、
  以下の関連記事を参照のこと

Snowflake ~ 文字列操作関連 ~
https://dk521123.hatenablog.com/entry/2022/10/01/000000

1)SQL

USE DATABASE test_db;
USE SCHEMA test_schema;

CREATE TEMPORARY STAGE IF NOT EXISTS test_stage
  URL = 's3://your-s3-bucket/test/demo/' -- ★ポイント[1]★
  FILE_FORMAT = (TYPE = JSON)
;

CREATE TABLE test_db.test_schema.user_table IF NOT EXISTS (
  user_id VARCHAR,
  user_name VARCHAR,
  city VARCHAR DEFAULT NULL
);

COPY INTO test_db.test_schema.user_table (
  user_id,
  user_name,
  city
)
FROM
(
  SELECT
    $1:user_id,
    $1:user_name,
    SPLIT_PART(SPLIT_PART(METADATA$FILENAME, '/', -2), '=', -1) AS city, -- ★ポイント[2]★
  FROM
    @test_stage
);

【3】使用した技術事項

1)メタデータ「METADATA$FILENAME」

* ファイル名(&パス)を取得できる

https://docs.snowflake.com/ja/user-guide/querying-metadata.html#metadata-columns

SQL

SELECT
  -- e.g. test/demo/city=tokyo/xxxx1.json.gz
  METADATA$FILENAME AS c1,
FROM
  @test_stage
;

2)SPLIT_PART()

* 指定された文字列を分割し、リクエストされた部分を返す。
* パラメーターが NULL の場合、 NULL が返す

https://docs.snowflake.com/ja/sql-reference/functions/split_part.html
構文

SPLIT_PART(<string>, <delimiter>, <partNumber>)
-- <string> : 対象テキスト
-- <delimiter> : 分割する区切り文字('--'のような文字列もOK)
-- <partNumber> : 取得したいインデックス

例1:Partitionの値を取得する経過

SELECT
  -- e.g. test/demo/city=tokyo/xxxx1.json.gz
  METADATA$FILENAME AS c1,
  -- e.g. city=tokyo
  SPLIT_PART(METADATA$FILENAME, '/', -2) AS c2,
  -- e.g. tokyo
  SPLIT_PART(SPLIT_PART(METADATA$FILENAME, '/', -2), '=', -1) AS city,
FROM
  @test_stage
;

例2:ファイル名取得

-- 例:s3://your-s3-bucket-name/a1/b1/c1/test_20220901.csv
SELECT
  SPLIT_PART(METADATA$FILENAME, '/', -1) -- ★「test_20220901.csv」
FROM
  @demo_temp2_stage;

関連記事

Snowflake ~ 基礎知識編 ~
https://dk521123.hatenablog.com/entry/2021/11/02/130111
Snowflake ~ 入門編 / Hello world
https://dk521123.hatenablog.com/entry/2021/11/22/212520
Snowflake ~ 基本編 / CREATE・ALTER TABLE ~
https://dk521123.hatenablog.com/entry/2022/09/03/012113
Snowflake ~ 基本編 / ステージ ~
https://dk521123.hatenablog.com/entry/2022/09/01/220643
Snowflake ~ ストレージ統合 ~
https://dk521123.hatenablog.com/entry/2022/06/29/221037
Snowflake ~ 文字列操作関連 ~
https://dk521123.hatenablog.com/entry/2022/10/01/000000