Superset บน Snowflake
การสร้าง dashboard ปฏิบัติการฝั่งต้นทางทั้งสามชุด: การเชื่อมต่อ, datasets, charts และการประกอบร่าง
คู่มือนี้พาคุณสร้าง dashboard ทั้งสามชุดใน Apache Superset ที่ http://localhost:8088 (admin / admin)
ลำดับการทำงาน:
- เริ่ม Superset และลงทะเบียนการเชื่อมต่อ Snowflake
- สร้าง dataset ทั้งหมด (query SQL ที่บันทึกไว้เป็น dataset ที่มีชื่อ)
- สร้าง chart และประกอบเป็น dashboard
ขั้นที่ 1: เริ่ม Superset และเชื่อมต่อกับ Snowflake
เริ่ม Superset
อิมเมจ Superset ถูกปรับแต่งให้มีไดรเวอร์ของ Snowflake และ ClickHouse อยู่แล้ว ใช้ --build ในการรันครั้งแรกเพื่อให้ Docker สร้างมัน:
cd "$(git rev-parse --show-toplevel)/workshop_public/snowflake_migration_lab/01-setup-snowflake/superset"
source ../.env
docker compose up -d --buildลงทะเบียนการเชื่อมต่อฐานข้อมูล (แบบอัตโนมัติ)
จากไดเรกทอรี superset/ ให้รัน:
source ../.env && ./init_superset.shสคริปต์จะรอให้ Superset พร้อม แล้วลงทะเบียน NYC Taxi — Snowflake (Source) ให้อัตโนมัติ ผลลัพธ์ที่คาดไว้:
>>> Superset is up.
>>> Authenticated.
>>> CSRF token obtained.
>>> Registering: NYC Taxi — Snowflake (Source)
Registered successfully.ลงทะเบียนการเชื่อมต่อฐานข้อมูล (ทางเลือกแบบทำมือ)
ถ้าคุณต้องการลงทะเบียนผ่าน UI:
- ไปที่ Settings → Database Connections → + Database
- เลือก Snowflake
- กรอก SQLAlchemy URI — เข้ารหัสอักขระพิเศษทุกตัวแบบ URL ในรหัสผ่านของคุณ (
#→%23,!→%21,@→%40เป็นต้น):
snowflake://<USER>:<URL_ENCODED_PASSWORD>@<SNOWFLAKE_ORG>-<SNOWFLAKE_ACCOUNT>/NYC_TAXI_DB/ANALYTICS?warehouse=ANALYTICS_WH&role=ANALYST_ROLE- ตั้ง Display Name:
NYC Taxi — Snowflake (Source) - ใต้ Advanced → SQL Lab: เปิด Allow this database to be explored และ Allow DML
- คลิก Test Connection → ควรแสดง "Connection looks good!"
- คลิก Connect
ขั้นที่ 2: สร้าง Dataset ทั้งหมด
chart ทั้งหมดใช้ virtual dataset — query SQL ที่บันทึกไว้เป็น dataset ที่มีชื่อ
วิธีสร้างแต่ละ dataset:
- ไปที่ Datasets → + Dataset
- เลือกฐานข้อมูล:
NYC Taxi — Snowflake (Source) - คลิก Create dataset from SQL query แล้ววาง SQL ด้านล่าง
- บันทึกด้วยชื่อที่ระบุไว้
- หลังบันทึก: ไปที่ Datasets → ไอคอนดินสอ → แท็บ Columns → "Sync columns from source" → Save ถ้าไม่ทำขั้นนี้ ตัวสร้าง chart จะแสดง 0 คอลัมน์
Dataset ของ Dashboard 1
ops_hourly_revenue — รายได้รายชั่วโมงแยกตามเขต
Schema: ANALYTICS
SELECT
DATE_TRUNC('hour', pickup_at) AS hour_bucket,
pickup_borough,
COUNT(*) AS trip_count,
SUM(total_amount_usd) AS total_revenue,
AVG(tip_amount_usd / NULLIF(fare_amount_usd, 0)) AS avg_tip_rate,
AVG(trip_distance_miles) AS avg_distance_miles
FROM ANALYTICS.FACT_TRIPS
WHERE pickup_at >= DATEADD('day', -7, CURRENT_TIMESTAMP())
AND pickup_borough IS NOT NULL
GROUP BY 1, 2
ORDER BY 1 DESC, total_revenue DESCops_zone_agg — ค่า aggregate ระดับโซน
Schema: ANALYTICS
SELECT
hour_bucket,
zone_id,
trips,
revenue,
avg_distance
FROM ANALYTICS.AGG_HOURLY_ZONE_TRIPS
WHERE hour_bucket >= DATEADD('day', -7, CURRENT_TIMESTAMP())ops_payment_split — สัดส่วนประเภทการชำระเงิน
Schema: ANALYTICS
SELECT
payment_type,
COUNT(*) AS trip_count,
SUM(total_amount_usd) AS total_revenue
FROM ANALYTICS.FACT_TRIPS
WHERE pickup_at >= DATEADD('day', -7, CURRENT_TIMESTAMP())
GROUP BY 1Dataset ของ Dashboard 2
exec_rolling_avg — ค่าเฉลี่ยเลื่อน 7 วัน
Schema: ANALYTICS
SELECT
pickup_at::DATE AS trip_date,
COUNT(*) AS daily_trip_count,
AVG(trip_distance_miles) AS daily_avg_distance,
AVG(AVG(trip_distance_miles)) OVER (
ORDER BY pickup_at::DATE
ROWS BETWEEN 6 PRECEDING AND CURRENT ROW
) AS rolling_7d_avg_distance,
SUM(total_amount_usd) AS daily_revenue,
SUM(SUM(total_amount_usd)) OVER (
ORDER BY pickup_at::DATE
ROWS BETWEEN 6 PRECEDING AND CURRENT ROW
) AS rolling_7d_revenue
FROM ANALYTICS.FACT_TRIPS
GROUP BY 1
ORDER BY 1 DESC
LIMIT 365exec_top_trips — 10 ทริปมูลค่าสูงสุดต่อเขต
Schema: ANALYTICS
ใช้
QUALIFYของ Snowflake — ความท้าทายสำคัญของการย้ายระบบ การเขียนใหม่บน ClickHouse ต้องใช้ subquery
SELECT
trip_id,
pickup_at,
pickup_borough,
total_amount_usd,
tip_amount_usd,
trip_distance_miles,
ROW_NUMBER() OVER (
PARTITION BY pickup_borough
ORDER BY total_amount_usd DESC
) AS rank_in_borough
FROM ANALYTICS.FACT_TRIPS
WHERE pickup_at::DATE = CURRENT_DATE() - 1
QUALIFY rank_in_borough <= 10
ORDER BY pickup_borough, rank_in_boroughexec_surge — ผลกระทบของราคาช่วงพีค
Schema: ANALYTICS
SELECT
CASE
WHEN surge_multiplier >= 2.0 THEN 'High Surge (2x+)'
WHEN surge_multiplier >= 1.5 THEN 'Medium Surge (1.5–2x)'
WHEN surge_multiplier > 1.0 THEN 'Low Surge (1–1.5x)'
ELSE 'No Surge (1x)'
END AS surge_category,
COUNT(*) AS trip_count,
ROUND(AVG(total_amount_usd), 2) AS avg_total_fare,
ROUND(AVG(fare_amount_usd), 2) AS avg_base_fare,
ROUND(AVG(surge_multiplier), 2) AS avg_surge
FROM ANALYTICS.FACT_TRIPS
WHERE surge_multiplier IS NOT NULL
GROUP BY 1
ORDER BY avg_surge DESCDataset ของ Dashboard 3
dqa_rating_dist — การกระจายคะแนนคนขับ
Schema: RAW ← เปลี่ยนค่านี้เมื่อสร้าง dataset
Query
RAW.TRIPS_RAWโดยตรงผ่านไวยากรณ์ colon-path ของ VARIANT นี่คือ query ที่ตั้งใจให้ช้า — เป้าหมายของ benchmark กับ ClickHouse
SELECT
ROUND(TRIP_METADATA:driver.rating::FLOAT, 1) AS rating_bucket,
COUNT(*) AS trip_count,
ROUND(AVG(TOTAL_AMOUNT), 2) AS avg_fare,
ROUND(AVG(DATEDIFF('minute', PICKUP_DATETIME, DROPOFF_DATETIME)), 1) AS avg_duration_minutes
FROM RAW.TRIPS_RAW
WHERE TRIP_METADATA:driver IS NOT NULL
AND TRIP_METADATA:driver.rating IS NOT NULL
GROUP BY 1
ORDER BY 1dqa_vehicle — รายได้ตามประเภทยานพาหนะ
Schema: ANALYTICS
SELECT
vehicle_type,
COUNT(*) AS trip_count,
SUM(total_amount_usd) AS total_revenue,
AVG(total_amount_usd) AS avg_fare,
AVG(trip_distance_miles) AS avg_distance
FROM ANALYTICS.FACT_TRIPS
WHERE vehicle_type IS NOT NULL
GROUP BY 1
ORDER BY total_revenue DESCdqa_traffic — ระดับการจราจรเทียบกับระยะเวลาเดินทาง
Schema: ANALYTICS
SELECT
traffic_level,
COUNT(*) AS trip_count,
AVG(duration_minutes) AS avg_duration_minutes,
AVG(trip_distance_miles) AS avg_distance_miles,
AVG(total_amount_usd) AS avg_fare
FROM ANALYTICS.FACT_TRIPS
WHERE traffic_level IS NOT NULL
GROUP BY 1
ORDER BY avg_duration_minutes DESCdqa_platform — แนวโน้มตามแพลตฟอร์มแอป
Schema: ANALYTICS
SELECT
pickup_at::DATE AS trip_date,
app_platform,
COUNT(*) AS trip_count,
AVG(surge_multiplier) AS avg_surge
FROM ANALYTICS.FACT_TRIPS
WHERE app_platform IS NOT NULL
AND pickup_at >= DATEADD('day', -30, CURRENT_TIMESTAMP())
GROUP BY 1, 2
ORDER BY 1 DESCขั้นที่ 3: สร้าง Dashboard
ตอนนี้ dataset ทั้ง 10 ชุดพร้อมแล้ว สร้าง chart และเพิ่มลงใน dashboard
Dashboard 1: Operations Command Center
วัตถุประสงค์: มุมมองปฏิบัติการแบบเรียลไทม์ที่แสดง 7 วันล่าสุด นี่คือ dashboard ชุดแรกที่พาร์ตเนอร์จะชี้ไปที่ ClickHouse ใน Part 2
สร้าง dashboard:
- Dashboards → + Dashboard
- Title:
Operations Command Center - Auto-refresh: ทุก 15 นาที (
···→ Edit dashboard → Auto-refresh)
Chart 1: Trips per Hour (Line chart)
- Chart type: Line Chart
- Dataset:
ops_hourly_revenue - X-axis:
hour_bucket - Metrics:
SUM(trip_count) - Series:
pickup_borough - Title:
Trips per Hour — Last 7 Days
Chart 2: Revenue by Borough (Bar chart)
- Chart type: Bar Chart
- Dataset:
ops_hourly_revenue - X-axis:
pickup_borough - Metrics:
SUM(total_revenue) - Sort: จากมากไปน้อยตาม metric
- Title:
Total Revenue by Borough — Last 7 Days
Chart 3: Payment Type Split (Pie chart)
- Chart type: Pie Chart
- Dataset:
ops_payment_split - Dimension:
payment_type - Metric:
SUM(trip_count) - Show labels: เปิด
- Title:
Trip Count by Payment Type
Chart 4: Total Trips (Big Number)
- Chart type: Big Number with Trendline
- Dataset:
ops_hourly_revenue - Metric:
SUM(trip_count) - Title:
Total Trips (Last 7 Days)
Chart 5: Borough Performance Summary (Table)
- Chart type: Table
- Dataset:
ops_hourly_revenue - Columns:
pickup_borough,SUM(trip_count),SUM(total_revenue),AVG(avg_tip_rate) - Row limit: 10
- Sort:
SUM(total_revenue)จากมากไปน้อย - Title:
Borough Performance Summary
เลย์เอาต์:
[ Total Trips — Big Number ] [ Total Revenue — Big Number (add 2nd) ]
[ Trips per Hour — Line chart (full width) ]
[ Revenue by Borough — Bar ] [ Payment Type Split — Pie ]
[ Borough Performance — Table (full width) ]Dashboard 2: Executive Weekly Report
วัตถุประสงค์: มุมมองเชิงกลยุทธ์สำหรับการทบทวนธุรกิจรายสัปดาห์ แสดงตัวอย่าง window function และไวยากรณ์เฉพาะของ Snowflake (QUALIFY) ที่ต้องเขียนใหม่ใน ClickHouse
สร้าง dashboard:
- Dashboards → + Dashboard
- Title:
Executive Weekly Report - Auto-refresh: 1 ชั่วโมง
Chart 6: Rolling 7-Day Revenue Trend (Line chart)
- Chart type: Line Chart
- Dataset:
exec_rolling_avg - X-axis:
trip_date - Metrics:
MAX(daily_revenue),MAX(rolling_7d_revenue) - Title:
Daily Revenue with 7-Day Rolling Average
Chart 7a: Daily Trip Volume (Big Number)
- Chart type: Big Number with Trendline
- Dataset:
exec_rolling_avg - Metric:
MAX(daily_trip_count) - Title:
Daily Trip Volume (Last Year)
Chart 7b: Rolling 7-Day Average Distance (Line chart)
- Chart type: Line Chart
- Dataset:
exec_rolling_avg - X-axis:
trip_date - Metrics:
MAX(rolling_7d_avg_distance) - Title:
Rolling 7-Day Average Distance (miles)
Chart 8: Top 10 Trips per Borough (Table)
- Chart type: Table
- Dataset:
exec_top_trips - Query Mode: RAW RECORDS ← สำคัญ: dataset นี้ใช้
QUALIFYดังนั้น Superset ต้องไม่ aggregate ซ้ำ - Columns:
pickup_borough,rank_in_borough,total_amount_usd,tip_amount_usd,trip_distance_miles,pickup_at - Sort By:
total_amount_usdจากมากไปน้อย - Row limit: 60
- Title:
Top 10 Trips per Borough — Yesterday - หมายเหตุ: ใช้
QUALIFY— เฉพาะของ Snowflake ต้องเขียนใหม่เป็น subquery สำหรับ ClickHouse
Chart 9: Surge Pricing Breakdown (Mixed chart)
- Chart type: Mixed Chart ← ใช้ตัวนี้ ไม่ใช่ Bar Chart เพราะ Bar Chart ไม่รองรับแกนรอง
- Dataset:
exec_surge - X-axis:
surge_category - Query A — Bar: metric
SUM(trip_count), labelTrip Count - Query B — Line: metric
MAX(avg_total_fare), labelAvg Total Fare, Y-axis: Right - Sort:
SUM(trip_count)จากมากไปน้อย - Title:
Trip Volume and Average Fare by Surge Category
Chart 10: Surge Distribution (Pie chart)
- Chart type: Pie Chart
- Dataset:
exec_surge - Dimension:
surge_category - Metric:
SUM(trip_count) - Title:
Surge Pricing Distribution
เลย์เอาต์:
[ Rolling Revenue — Line chart (full width) ]
[ Daily Trip Volume — Big Number (50%) ] [ Avg Distance — Line (50%) ]
[ Top 10 Trips — Table (60%) ] [ Surge Distribution — Pie (40%) ]
[ Surge Breakdown — Bar chart (full width) ]Dashboard 3: Driver & Quality Analytics
วัตถุประสงค์: เจาะลึกประสิทธิภาพคนขับและคุณภาพของทริป ตั้งใจให้เป็น dashboard ที่ช้าที่สุด — query RAW.TRIPS_RAW โดยตรงผ่านการเข้าถึง VARIANT บันทึกเวลา query ที่นี่ไว้เป็นค่าอ้างอิงพื้นฐานสำหรับ benchmark ประสิทธิภาพของ ClickHouse ใน Part 2
สร้าง dashboard:
- Dashboards → + Dashboard
- Title:
Driver & Quality Analytics - Auto-refresh: 1 ชั่วโมง
Chart 11: Trip Count by Driver Rating (Bar chart)
- Chart type: Bar Chart
- Dataset:
dqa_rating_dist - X-axis:
rating_bucket - Metrics:
SUM(trip_count) - Title:
Trip Count by Driver Rating - หมายเหตุ: สแกน
RAW.TRIPS_RAWด้วยการเข้าถึง VARIANT — สังเกตเวลา query เทียบกับ ClickHouse
Chart 12: Average Fare by Rating (Line chart)
- Chart type: Line Chart
- Dataset:
dqa_rating_dist - X-axis:
rating_bucket - Metrics:
MAX(avg_fare) - Title:
Average Fare by Driver Rating
Chart 13: Revenue by Vehicle Type (Horizontal bar)
- Chart type: Bar Chart (แนวนอน)
- Dataset:
dqa_vehicle - X-axis:
vehicle_type - Metrics:
SUM(total_revenue),SUM(trip_count)(แกนรอง) - Title:
Revenue and Trip Count by Vehicle Type
Chart 14: Traffic Level Impact (Bar chart)
- Chart type: Bar Chart
- Dataset:
dqa_traffic - X-axis:
traffic_level - Metrics:
MAX(avg_duration_minutes),MAX(avg_distance_miles)(แกนรอง) - Title:
Average Trip Duration and Distance by Traffic Level
Chart 15: Daily Trips by App Platform (Line chart)
- Chart type: Line Chart
- Dataset:
dqa_platform - X-axis:
trip_date - Metrics:
SUM(trip_count) - Series:
app_platform - Title:
Daily Trips by App Platform — Last 30 Days
Chart 16: Surge by Platform (Table)
- Chart type: Table
- Dataset:
dqa_platform - Columns:
app_platform,SUM(trip_count),AVG(avg_surge) - Row limit: 10
- Title:
Surge by Platform
เลย์เอาต์:
[ Trip Count by Rating — Bar ] [ Avg Fare by Rating — Line ]
[ Revenue by Vehicle Type — Horizontal bar (full width) ]
[ Traffic Level Impact — Bar (50%) ] [ Surge by Platform — Table (50%)]
[ Daily Trips by Platform — Line chart (full width) ]ขั้นที่ 4: ตรวจสอบ
- เปิด dashboard แต่ละชุดและยืนยันว่า chart ทั้งหมดโหลดได้โดยไม่มีข้อผิดพลาด
- สำหรับ Dashboard 3 ให้จดเวลาที่ใช้รัน query ของ
dqa_rating_distใน Snowflake UI → Activity → Query History — เก็บค่านี้ไว้เป็น benchmark ของการย้ายระบบ

ขั้นที่ 5: ส่งออกเพื่อนำกลับมาใช้
เมื่อ dashboard เสร็จสมบูรณ์ ให้ส่งออกไว้เพื่อให้การรันครั้งถัด ๆ ไปนำเข้าได้อัตโนมัติ:
- เปิด dashboard แต่ละชุด →
···→ Export (บันทึกเป็น.zip) - วางไฟล์ไว้ใน
superset/dashboards/:01_operations_command_center.zip02_executive_weekly_report.zip03_driver_quality_analytics.zip
- รัน
./init_superset.shอีกครั้ง — มันจะนำเข้าไฟล์เหล่านี้ให้อัตโนมัติในการ setup ครั้งถัดไป
สำคัญ — credential ตัวอย่างใน ZIP ที่ commit ไว้
*.zipแต่ละไฟล์ที่ commit ไว้มีการเชื่อมต่อฐานข้อมูลถูกปิดบังไว้ในdatabases/*.yaml:sqlalchemy_uri: snowflake://LAB_USER:XXXXXXXXXX@MYORG-MYACCOUNT/NYC_TAXI_DB/ANALYTICS?role=ANALYST_ROLE&warehouse=ANALYTICS_WH
- การนำเข้าอัตโนมัติ (
./init_superset.sh) — ใช้ได้ทันที สคริปต์จะลงทะเบียนการเชื่อมต่อ Snowflake จริงจาก.envก่อน การนำเข้า แล้วใส่ URI ที่ถูกต้องกลับ หลัง การนำเข้าแต่ละครั้ง (ดู_update_dbในinit_superset.sh) ดังนั้นค่าตัวอย่างจะถูกเขียนทับด้วย credential จริงของคุณ- การนำเข้าด้วยมือผ่าน Superset UI — ฐานข้อมูลที่นำเข้าจะถูกสร้างด้วย URI ตัวอย่างและเชื่อมต่อไม่ได้ หลังนำเข้า ให้ไปที่ Settings → Database Connections → Edit รายการนั้นและแทน
sqlalchemy_uriด้วย URI ของ Snowflake จริงของคุณ (เช่นsnowflake://<USER>:<PASSWORD>@<ORG>-<ACCOUNT>/NYC_TAXI_DB/ANALYTICS?role=ANALYST_ROLE&warehouse=ANALYTICS_WH)- การส่งออก dashboard ของคุณเองอีกครั้ง — Superset ฝัง account locator และชื่อผู้ใช้ของคุณลงใน
databases/*.yamlตอนส่งออก ก่อน commit ZIP ที่คุณส่งออกใหม่ ให้ปิดบังค่าเหล่านั้นกลับไปเป็นMYORG-MYACCOUNT/LAB_USERเพื่อไม่ให้ตัวระบุบัญชีของคุณรั่วเข้าไปในประวัติ git
การดำเนินงาน ClickHouse
การรันเซอร์วิสเป้าหมาย: การคิวรี, การเฝ้าดู merge และ part, dictionary และนิสัยการดำเนินงานที่ต่างจาก Snowflake
Superset บน ClickHouse
การสร้าง dashboard ใหม่บน ClickHouse: เจ็ด dataset ที่ใช้งาน uniqHLL12, quantileTDigest, การสุ่มตัวอย่าง, window function และ dictGet แล้วต่อด้วย 18 chart กระจายใน dashboard สี่ชุด