❄️

【SWTTokyo25レポ】UserCommunityHub : FrostyFriday〜生FrostyFridayやったよ!

がく@ちゅらデータエンジニアです。
こんにちわっ!

dbt Meetup #16 : dbt Tokyo crew就任&トリスタンCEO&東條Anthropic新社長にあう
→ Snowflake World Tour Tokyo 2025 Day 1 生FrostyFridayやったった
→ Snowflake World Tour Tokyo 2025 Day 2
→ (土曜日)1日中くたばってた
→ (日曜日)ほぼくたばってた
→ (祝日)子どもの体調があやしくて、外出せず。レポブログを結構書いた

ってことで、あまりに浦島状態で、仕事のエンジンが全然掛かりませんね!

承前:SWTTokyo25のコミュニティブースでFrostyFridayの時間をもらえた

7月某日、Slackにて、swt-2025-community-boothにて、Snowvillageのユーザ活動でブースで発表しませんか?ってお話がありました。
その中でFrosty Fridayでも1枠いただけることになりました

ちなみに、40分!!!!
最初はもっと短い時間で、
これまでの出演者などを繋いで、Frosty Fridayの紹介動画を繰り返し流すか〜
みたいな話をしていたのです。

けど40分ってことで・・・
「もしかして、これ生FrostyFridayできるんじゃね?」
自分で自分の首を絞めたやつ

当日

EXPO会場にSNOWFLAKE COMMUNITY HUBの様子

事前告知もそれほどなく、コミュニティーブースでゲリラライブをやるような
ブースジャック状態!

生FrostyFridayLiveChallange!

まずこんな感じでスタート

内容としては

  • MCの紹介
  • Frosty Fridayって知ってますか?
  • Frosty Friday Live Challangeとは
  • 今日は生FrostyFridayやります!

という形で進めました。

MC自己紹介

ここで会場的な問題が・・・・
ブース前でやっているので、マイクがないんです・・・マイクが

私は大声を張り上げてお話をさせていただいたのですが、
あれさん、tomoさんが地声で話すのはかなーーり厳しい・・・
※あれさんは、個人で小さなスピーカーを持ってきてくださってましたが、それでもなかなかきつい

結局、今回はほぼ私がやらせていただきましたm(__)m

Frosty Fridayとは

https://frostyfriday.org/

こちらで隔週出題されていますね!
2025年9月時点で148問作成されています、ほんとすごい!継続すごい!!

Frosty Friday Live Challangeとは

こちらは、隔週木曜日22時からYoutube Liveで配信している
What's New + Topics & Frosty Friday 2Week分 をやってます

https://www.youtube.com/playlist?list=PLVj4iIZgzTAq2FzaBBgqFOtZaJTcoG3JR

こちら最新で34回の配信をしてます
※ウォ、こんなにやってたんかぁ、すげぇぇ(自画自賛)

生FrostyFriday

今回は
Frosty Fridayから、Week1 Basic - External Stages

SWTT特別問題として、百人一首分析(AISQLを使おう!)
の2題をやりました。
百人一首分析は、私が作ったんだぜぇ!いえーい!

Week1 Basic - External Stages

解法は2つ

  • 解法① 基本的な解法
  • 解法② INFER_SCHEMA を使った解法

まずは、提供されているS3バケットをStageに取り込みます

-- stageの作成
-- s3://frostyfridaychallenges/challenge_1/

create or replace stage week001_stage
    URL='s3://frostyfridaychallenges/challenge_1/';

-- Stage上のファイルを確認
list @week001_stage;

この用に3ファイルが配置されているのがわかりますね

解法① 基本的な解法

https://github.com/gakut12/Frosty-Friday/blob/main/week001_basic_external_stages/week1_swttokyo25_1.sql

こちらの解法の肝は、SKIP_HEADER=1 です

-- file formatの作成
create or replace file format week1_csv_format
  type = CSV
  skip_header = 1
  null_if = ('NULL', 'null')
  empty_field_as_null = true
;

取り込むときは、メタデータを一緒の取り込みましょう
ファイル名、ファイルでの何行目か は役に立つことが多いです

解法② INFER_SCHEMA を使った解法

https://github.com/gakut12/Frosty-Friday/blob/main/week001_basic_external_stages/week1_swttokyo25_2_infer_schema.sql

こちらでは

  • File Format : PARSE_HEADER=TRUE
  • File Format : Error_on_column_count_mismatch
  • CREATE TABLE USING TEMPLAGE

のテクを使ってます
また、PARSE_HEADERと、メタデータを自動的に取り込むように、工夫をこらしてます

create or replace table week1_table_2
using template (
    select 
        array_cat (
            array_agg(object_construct('COLUMN_NAME', column_name, 'TYPE', type, 'NULLABLE', nullable))
            -- * にすると16MBを超える場合もあるので、カラムを絞る
            , [
            {'COLUMN_NAME':'FILENAME', 'TYPE':'STRING', 'NULLABLE':true}
            , {'COLUMN_NAME':'FILE_ROW_NUMBER', 'TYPE':'NUMBER', 'NULLABLE':true}
            , {'COLUMN_NAME':'START_SCAN_TIME', 'TYPE':'TIMESTAMP_LTZ', 'NULLABLE':true}
        ]::variant
    )
    from table (
        infer_schema (
            location => '@week001_stage'
            , file_format => 'week3_csv_format_2'
            , ignore_case => true
        )
    )
);

copy into week1_table_2 
from 
    @week001_stage
match_by_column_name = case_insensitive
files = ('1.csv','2.csv','3.csv')
file_format = (FORMAT_NAME = 'week1_csv_format_2')
include_metadata = (
   filename = METADATA$FILENAME
   , file_row_number = METADATA$FILE_ROW_NUMBER
   , start_scan_time = METADATA$START_SCAN_TIME
)
;

SWTTokyo25特別問題 百人一首分析:AISQLを使おう

こんな問題を作りました

https://github.com/fumiama/OguraHyakuninIsshu

小倉百人一首のデータは、こちらからCSVを頂きました、ほんとにありがとうございます!!
こちらをSnowsightから投入しました。

分析には、AISQLを使ってみます

  • AI_CLASSIFYで分類
  • AI_FILTERでより深く分析
  • AI_SENTIMENTで感情分析

https://github.com/gakut12/Frosty-Friday/blob/main/swttokyo25/hyakunin_isshu.sql

SELECT
  *
FROM
  "FROSTY_FRIDAY_DB"."SWTT_SPECIAL_1"."HYAKUNIN_ISSYU"
LIMIT
  10;


百人一首がテーブルに入っていますね

AI_CLASSIFY

select 
    AI_CLASSIFY(
        UPPER_PHRASE || LOWER_PHRASE,
        ['春','夏', '秋', '冬']
    ):labels[0]::string as season
    , count(*) as count
from 
    HYAKUNIN_ISSYU
group by all
order by count;

こちらで、春夏秋冬に分類をしてみますた

結構きれいに分類できてそうです・・・・

と冬が異常に多くなってます。
これは、季節が判断できないものが全部「冬」になってしまっているからかもしれません
※AI_CLASSIFYは、プロンプトを指定できないので、このあたりは難しいのかなと思いました。機能が悪いわけではない

AI_FILTER

with analyse as (
select 
    no
    , upper_phrase || ' ' || lower_phrase as content
    , ai_filter(
        prompt('これは春についての文章ですか?:{0}', content)
    ) as is_spring
    , ai_filter(
        prompt('これは夏についての文章ですか?:{0}', content)
    ) as is_summer
    , ai_filter(
        prompt('これは秋についての文章ですか?:{0}', content)
    ) as is_autumn
    , ai_filter(
        prompt('これは冬についての文章ですか?:{0}', content)
    ) as is_winter
    , ai_filter(
        prompt('これは季節についての文章ですか?:{0}', content)
    ) as is_season
 from 
    hyakunin_issyu
) 

こんな感じで、プロンプトを入れて、春夏秋冬を判断してもらいました。

結果

と、AI_CLASSIFYに比べるとだいぶマッチしてきました
多分ですが、プロンプトをうまく調整すればもうちょっと良くなりそうな気がしますね

AI_SENTIMENT

with sentiment as (
select
    no
    , upper_phrase || ' ' || lower_phrase as content
    , ai_sentiment(
        content
    ):categories[0]:sentiment::string as feeling_category
from 
    hyakunin_issyu
) 
-- select * from sentiment
select feeling_category, count(*) as count, '' as memo  from sentiment
group by all
;

AI_SENTIMENTもデフォルトの使い方をしたので、このような判定をしています

昔から歌にはネガティブなパッションを込めたってことなのかもしれないww

当日の様子

皆様、見ていただいてほんとーーーーーにありがとうございました!!!!!

ちゅらデータ株式会社

Discussion