Snowflake MigrationClickHouse Workshops

Superset trên Snowflake

Xây dựng ba dashboard vận hành phía nguồn: kết nối, dataset, chart và lắp ghép.

Hướng dẫn này đi qua từng bước để tạo cả ba dashboard trong Apache Superset tại http://localhost:8088 (admin / admin).

Thứ tự các bước:

  1. Khởi động Superset và đăng ký kết nối Snowflake
  2. Tạo toàn bộ dataset (các truy vấn SQL được lưu thành dataset có tên)
  3. Dựng chart và lắp ghép dashboard

Bước 1: Khởi Động Superset và Kết Nối Tới Snowflake

Khởi động Superset

Image Superset đã được tùy biến để bao gồm driver cho Snowflake và ClickHouse. Dùng --build ở lần chạy đầu để Docker build nó:

cd "$(git rev-parse --show-toplevel)/workshop_public/snowflake_migration_lab/01-setup-snowflake/superset"
source ../.env
docker compose up -d --build

Đăng ký kết nối database (tự động)

Từ thư mục superset/, chạy:

source ../.env && ./init_superset.sh

Script sẽ đợi Superset sẵn sàng, rồi tự động đăng ký NYC Taxi — Snowflake (Source). Kết quả mong đợi:

>>> Superset is up.
>>> Authenticated.
>>> CSRF token obtained.
>>> Registering: NYC Taxi — Snowflake (Source)
    Registered successfully.

Đăng ký kết nối database (cách thủ công thay thế)

Nếu bạn muốn đăng ký qua UI:

  1. Vào Settings → Database Connections → + Database
  2. Chọn Snowflake
  3. Điền SQLAlchemy URI — URL-encode mọi ký tự đặc biệt trong mật khẩu của bạn (# → %23, ! → %21, @ → %40, v.v.):
snowflake://<USER>:<URL_ENCODED_PASSWORD>@<SNOWFLAKE_ORG>-<SNOWFLAKE_ACCOUNT>/NYC_TAXI_DB/ANALYTICS?warehouse=ANALYTICS_WH&role=ANALYST_ROLE
  1. Đặt Display Name: NYC Taxi — Snowflake (Source)
  2. Trong Advanced → SQL Lab: bật Allow this database to be explored và Allow DML
  3. Bấm Test Connection → phải hiện "Connection looks good!"
  4. Bấm Connect

Bước 2: Tạo Toàn Bộ Dataset

Mọi chart đều dùng virtual dataset — các truy vấn SQL được lưu thành dataset có tên.

Cách tạo từng dataset:

  1. Vào Datasets → + Dataset
  2. Chọn database: NYC Taxi — Snowflake (Source)
  3. Bấm Create dataset from SQL query và dán đoạn SQL bên dưới
  4. Lưu với tên được nêu
  5. Sau khi lưu: vào Datasets → pencil icon → Columns tab → "Sync columns from source" → Save. Không có bước này, trình dựng chart sẽ hiện 0 cột.

Dataset cho Dashboard 1

ops_hourly_revenue — Doanh thu theo giờ theo borough

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 DESC

ops_zone_agg — Tổng hợp theo zone

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 — Phân tách theo loại thanh toán

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 1

Dataset cho Dashboard 2

exec_rolling_avg — Trung bình trượt 7 ngày

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 365

exec_top_trips — Top 10 chuyến đi giá trị cao nhất theo borough

Schema: ANALYTICS

Dùng QUALIFY của Snowflake — một thách thức di trú then chốt. Bản viết lại cho ClickHouse đòi hỏi một 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_borough

exec_surge — Tác động của giá 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 DESC

Dataset cho Dashboard 3

dqa_rating_dist — Phân phối điểm đánh giá tài xế

Schema: RAW ← đổi giá trị này khi tạo dataset

Truy vấn trực tiếp RAW.TRIPS_RAW qua cú pháp colon-path của VARIANT. Đây là truy vấn chậm một cách có chủ ý — mục tiêu benchmark cho 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 1

dqa_vehicle — Doanh thu theo loại xe

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 DESC

dqa_traffic — Mức độ giao thông so với thời lượng chuyến đi

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 DESC

dqa_platform — Xu hướng theo nền tảng ứng dụng

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

Bước 3: Dựng Dashboard

Cả 10 dataset giờ đã sẵn sàng. Hãy tạo chart và thêm chúng vào dashboard.


Dashboard 1: Operations Command Center

Mục đích: Góc nhìn vận hành thời gian thực cho 7 ngày gần nhất. Đây là dashboard đầu tiên mà đối tác chuyển hướng sang ClickHouse trong Phần 2.

Tạo dashboard:

  1. Dashboards → + Dashboard
  2. Title: Operations Command Center
  3. Auto-refresh: mỗi 15 phút (··· → 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: giảm dần theo 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: bật
  • 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) giảm dần
  • Title: Borough Performance Summary

Bố cục:

[ 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

Mục đích: Góc nhìn chiến lược cho buổi rà soát kinh doanh hằng tuần. Trình diễn các window function và cú pháp đặc thù Snowflake (QUALIFY) đòi hỏi phải viết lại trong ClickHouse.

Tạo dashboard:

  1. Dashboards → + Dashboard
  2. Title: Executive Weekly Report
  3. Auto-refresh: 1 giờ

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 ← quan trọng: dataset dùng QUALIFY nên Superset không được tổng hợp lại
  • Columns: pickup_borough, rank_in_borough, total_amount_usd, tip_amount_usd, trip_distance_miles, pickup_at
  • Sort By: total_amount_usd giảm dần
  • Row limit: 60
  • Title: Top 10 Trips per Borough — Yesterday
  • Ghi chú: Dùng QUALIFY — đặc thù Snowflake, phải được viết lại thành subquery cho ClickHouse

Chart 9: Surge Pricing Breakdown (Mixed chart)

  • Chart type: Mixed Chart ← dùng loại này, không dùng Bar Chart; Bar Chart không hỗ trợ trục phụ
  • Dataset: exec_surge
  • X-axis: surge_category
  • Query A — Bar: metric SUM(trip_count), nhãn Trip Count
  • Query B — Line: metric MAX(avg_total_fare), nhãn Avg Total Fare, Y-axis: Right
  • Sort: SUM(trip_count) giảm dần
  • 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

Bố cục:

[ 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

Mục đích: Đào sâu vào hiệu suất tài xế và chất lượng chuyến đi. Đây là dashboard chậm nhất một cách có chủ ý — truy vấn trực tiếp RAW.TRIPS_RAW qua truy cập VARIANT. Hãy ghi lại thời gian truy vấn ở đây làm đường cơ sở cho benchmark hiệu năng ClickHouse trong Phần 2.

Tạo dashboard:

  1. Dashboards → + Dashboard
  2. Title: Driver & Quality Analytics
  3. Auto-refresh: 1 giờ

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
  • Ghi chú: Scan RAW.TRIPS_RAW với truy cập VARIANT — hãy quan sát thời gian truy vấn so với 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 (horizontal)
  • Dataset: dqa_vehicle
  • X-axis: vehicle_type
  • Metrics: SUM(total_revenue), SUM(trip_count) (trục phụ)
  • 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) (trục phụ)
  • 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

Bố cục:

[ 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)                    ]

Bước 4: Kiểm Chứng

  1. Mở từng dashboard và xác nhận mọi chart đều tải mà không có lỗi
  2. Với Dashboard 3, ghi lại thời gian thực thi truy vấn dqa_rating_dist trong Snowflake UI → Activity → Query History — hãy lưu con số này làm benchmark di trú của bạn

Chart Superset hiển thị phân phối điểm đánh giá tài xế trên tập dữ liệu Snowflake, tập trung trong khoảng 4.0 đến 5.0

Bước 5: Xuất Ra Để Tái Sử Dụng

Khi các dashboard đã hoàn tất, hãy xuất chúng ra để những lần chạy sau có thể tự động import:

  1. Mở từng dashboard → ··· → Export (lưu thành .zip)
  2. Đặt các file vào superset/dashboards/:
    • 01_operations_command_center.zip
    • 02_executive_weekly_report.zip
    • 03_driver_quality_analytics.zip
  3. Chạy lại ./init_superset.sh — nó sẽ tự động import chúng ở các lần setup sau

Quan trọng — thông tin đăng nhập giả trong các ZIP đã commit.

Mỗi *.zip đã commit đều có kết nối database bị che trong databases/*.yaml:

sqlalchemy_uri: snowflake://LAB_USER:XXXXXXXXXX@MYORG-MYACCOUNT/NYC_TAXI_DB/ANALYTICS?role=ANALYST_ROLE&warehouse=ANALYTICS_WH
  • Auto-import (./init_superset.sh) — hoạt động nguyên trạng. Script đăng ký kết nối Snowflake thật từ .env trước khi import, rồi áp lại URI đúng sau mỗi lần import (xem _update_db trong init_superset.sh), nên các giá trị giả bị ghi đè bằng thông tin đăng nhập thật của bạn.
  • Import thủ công qua Superset UI — database được import sẽ được tạo với URI giả và sẽ không kết nối được. Sau khi import, vào Settings → Database Connections → Edit mục đó và thay sqlalchemy_uri bằng URI Snowflake thật của bạn (ví dụ snowflake://<USER>:<PASSWORD>@<ORG>-<ACCOUNT>/NYC_TAXI_DB/ANALYTICS?role=ANALYST_ROLE&warehouse=ANALYTICS_WH).
  • Xuất lại dashboard của riêng bạn — Superset nhúng account locator và username của bạn vào databases/*.yaml khi export. Trước khi commit các ZIP xuất lại, hãy che các giá trị đó trở về MYORG-MYACCOUNT / LAB_USER để định danh tài khoản của bạn không lọt vào git history.

Trên trang này

Bước 1: Khởi Động Superset và Kết Nối Tới SnowflakeKhởi động SupersetĐăng ký kết nối database (tự động)Đăng ký kết nối database (cách thủ công thay thế)Bước 2: Tạo Toàn Bộ DatasetDataset cho Dashboard 1ops_hourly_revenue — Doanh thu theo giờ theo boroughops_zone_agg — Tổng hợp theo zoneops_payment_split — Phân tách theo loại thanh toánDataset cho Dashboard 2exec_rolling_avg — Trung bình trượt 7 ngàyexec_top_trips — Top 10 chuyến đi giá trị cao nhất theo boroughexec_surge — Tác động của giá surgeDataset cho Dashboard 3dqa_rating_dist — Phân phối điểm đánh giá tài xếdqa_vehicle — Doanh thu theo loại xedqa_traffic — Mức độ giao thông so với thời lượng chuyến đidqa_platform — Xu hướng theo nền tảng ứng dụngBước 3: Dựng DashboardDashboard 1: Operations Command CenterChart 1: Trips per Hour (Line chart)Chart 2: Revenue by Borough (Bar chart)Chart 3: Payment Type Split (Pie chart)Chart 4: Total Trips (Big Number)Chart 5: Borough Performance Summary (Table)Dashboard 2: Executive Weekly ReportChart 6: Rolling 7-Day Revenue Trend (Line chart)Chart 7a: Daily Trip Volume (Big Number)Chart 7b: Rolling 7-Day Average Distance (Line chart)Chart 8: Top 10 Trips per Borough (Table)Chart 9: Surge Pricing Breakdown (Mixed chart)Chart 10: Surge Distribution (Pie chart)Dashboard 3: Driver & Quality AnalyticsChart 11: Trip Count by Driver Rating (Bar chart)Chart 12: Average Fare by Rating (Line chart)Chart 13: Revenue by Vehicle Type (Horizontal bar)Chart 14: Traffic Level Impact (Bar chart)Chart 15: Daily Trips by App Platform (Line chart)Chart 16: Surge by Platform (Table)Bước 4: Kiểm ChứngBước 5: Xuất Ra Để Tái Sử Dụng
VI