Snowflake MigrationClickHouse Workshops

Superset en Snowflake

Cómo crear los tres paneles operativos del origen: conexión, conjuntos de datos, gráficos y montaje.

Esta guía explica cómo crear los tres paneles en Apache Superset en http://localhost:8088 (admin / admin).

Orden de trabajo:

  1. Inicia Superset y registra la conexión con Snowflake
  2. Crea todos los conjuntos de datos (consultas SQL guardadas con nombre)
  3. Crea los gráficos y monta los paneles

Paso 1: iniciar Superset y conectarlo a Snowflake

Iniciar Superset

La imagen de Superset está personalizada para incluir los controladores de Snowflake y ClickHouse. Usa --build en la primera ejecución para que Docker la construya:

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

Registrar la conexión a la base de datos (automático)

Desde el directorio superset/, ejecuta:

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

El script espera a que Superset esté listo y registra automáticamente NYC Taxi — Snowflake (Source). Salida esperada:

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

Registrar la conexión a la base de datos (alternativa manual)

Si prefieres hacerlo desde la interfaz:

  1. Ve a Settings → Database Connections → + Database
  2. Selecciona Snowflake
  3. Introduce el URI de SQLAlchemy; codifica en URL los caracteres especiales de la contraseña (# → %23, ! → %21, @ → %40, etc.):
snowflake://<USER>:<URL_ENCODED_PASSWORD>@<SNOWFLAKE_ORG>-<SNOWFLAKE_ACCOUNT>/NYC_TAXI_DB/ANALYTICS?warehouse=ANALYTICS_WH&role=ANALYST_ROLE
  1. Define Display Name como NYC Taxi — Snowflake (Source)
  2. En Advanced → SQL Lab, activa Allow this database to be explored y Allow DML
  3. Haz clic en Test Connection; debe mostrar "Connection looks good!"
  4. Haz clic en Connect

Paso 2: crear todos los conjuntos de datos

Todos los gráficos usan conjuntos virtuales: consultas SQL guardadas con un nombre.

Cómo crear cada conjunto:

  1. Ve a Datasets → + Dataset
  2. Selecciona la base NYC Taxi — Snowflake (Source)
  3. Haz clic en Create dataset from SQL query y pega el SQL siguiente
  4. Guárdalo con el nombre indicado
  5. Después de guardar, ve a Datasets → icono de lápiz → pestaña Columns → "Sync columns from source" → Save. Sin este paso, el editor mostrará 0 columnas.

Conjuntos de datos del panel 1

ops_hourly_revenue: ingresos horarios por distrito

Esquema: 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: agregados por zona

Esquema: 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: distribución por tipo de pago

Esquema: 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

Conjuntos de datos del panel 2

exec_rolling_avg: medias móviles de 7 días

Esquema: 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: los 10 viajes de mayor valor por distrito

Esquema: ANALYTICS

Usa QUALIFY de Snowflake, uno de los retos principales de la migración. En ClickHouse debe reescribirse mediante una subconsulta.

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: impacto de las tarifas dinámicas

Esquema: 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

Conjuntos de datos del panel 3

dqa_rating_dist: distribución de valoraciones de conductores

Esquema: RAW ← cámbialo al crear el conjunto

Consulta RAW.TRIPS_RAW directamente con la sintaxis de ruta de VARIANT. Es la consulta lenta intencional, usada como objetivo del benchmark de 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: ingresos por tipo de vehículo

Esquema: 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: nivel de tráfico frente a duración del viaje

Esquema: 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: tendencias por plataforma de la aplicación

Esquema: 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

Paso 3: crear los paneles

Los 10 conjuntos de datos están listos. Crea los gráficos y añádelos a sus paneles.


Panel 1: Operations Command Center

Objetivo: vista operativa en tiempo real de los últimos siete días. Es el primer panel que los socios redirigen a ClickHouse en la Parte 2.

Crear el panel:

  1. Dashboards → + Dashboard
  2. Título: Operations Command Center
  3. Actualización automática cada 15 minutos (··· → Edit dashboard → Auto-refresh)

Gráfico 1: viajes por hora (líneas)

  • Tipo de gráfico: Line Chart
  • Conjunto: ops_hourly_revenue
  • Eje X: hour_bucket
  • Métricas: SUM(trip_count)
  • Series: pickup_borough
  • Título: Trips per Hour — Last 7 Days

Gráfico 2: ingresos por distrito (barras)

  • Tipo de gráfico: Bar Chart
  • Conjunto: ops_hourly_revenue
  • Eje X: pickup_borough
  • Métricas: SUM(total_revenue)
  • Orden: descendente por métrica
  • Título: Total Revenue by Borough — Last 7 Days

Gráfico 3: distribución por tipo de pago (circular)

  • Tipo de gráfico: Pie Chart
  • Conjunto: ops_payment_split
  • Dimensión: payment_type
  • Métrica: SUM(trip_count)
  • Mostrar etiquetas: activado
  • Título: Trip Count by Payment Type

Gráfico 4: viajes totales (número grande)

  • Tipo de gráfico: Big Number with Trendline
  • Conjunto: ops_hourly_revenue
  • Métrica: SUM(trip_count)
  • Título: Total Trips (Last 7 Days)

Gráfico 5: resumen de rendimiento por distrito (tabla)

  • Tipo de gráfico: Table
  • Conjunto: ops_hourly_revenue
  • Columnas: pickup_borough, SUM(trip_count), SUM(total_revenue), AVG(avg_tip_rate)
  • Límite de filas: 10
  • Orden: SUM(total_revenue) descendente
  • Título: Borough Performance Summary

Disposición:

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

Panel 2: Executive Weekly Report

Objetivo: vista estratégica para la revisión semanal del negocio. Muestra funciones de ventana y sintaxis específica de Snowflake (QUALIFY) que deben reescribirse en ClickHouse.

Crear el panel:

  1. Dashboards → + Dashboard
  2. Título: Executive Weekly Report
  3. Actualización automática: 1 hora

Gráfico 6: tendencia móvil de ingresos de 7 días (líneas)

  • Tipo de gráfico: Line Chart
  • Conjunto: exec_rolling_avg
  • Eje X: trip_date
  • Métricas: MAX(daily_revenue), MAX(rolling_7d_revenue)
  • Título: Daily Revenue with 7-Day Rolling Average

Gráfico 7a: volumen diario de viajes (número grande)

  • Tipo de gráfico: Big Number with Trendline
  • Conjunto: exec_rolling_avg
  • Métrica: MAX(daily_trip_count)
  • Título: Daily Trip Volume (Last Year)

Gráfico 7b: distancia media móvil de 7 días (líneas)

  • Tipo de gráfico: Line Chart
  • Conjunto: exec_rolling_avg
  • Eje X: trip_date
  • Métricas: MAX(rolling_7d_avg_distance)
  • Título: Rolling 7-Day Average Distance (miles)

Gráfico 8: 10 viajes principales por distrito (tabla)

  • Tipo de gráfico: Table
  • Conjunto: exec_top_trips
  • Modo de consulta: RAW RECORDS ← importante: el conjunto usa QUALIFY y Superset no debe volver a agregar
  • Columnas: pickup_borough, rank_in_borough, total_amount_usd, tip_amount_usd, trip_distance_miles, pickup_at
  • Ordenar por: total_amount_usd descendente
  • Límite de filas: 60
  • Título: Top 10 Trips per Borough — Yesterday
  • Nota: usa QUALIFY, específico de Snowflake; debe reescribirse como subconsulta para ClickHouse

Gráfico 9: desglose de tarifas dinámicas (gráfico mixto)

  • Tipo de gráfico: Mixed Chart ← usa este, no Bar Chart; este último no admite un eje secundario
  • Conjunto: exec_surge
  • Eje X: surge_category
  • Consulta A — Barra: métrica SUM(trip_count), etiqueta Trip Count
  • Consulta B — Línea: métrica MAX(avg_total_fare), etiqueta Avg Total Fare, eje Y: Right
  • Orden: SUM(trip_count) descendente
  • Título: Trip Volume and Average Fare by Surge Category

Gráfico 10: distribución de tarifas dinámicas (circular)

  • Tipo de gráfico: Pie Chart
  • Conjunto: exec_surge
  • Dimensión: surge_category
  • Métrica: SUM(trip_count)
  • Título: Surge Pricing Distribution

Disposición:

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

Panel 3: Driver & Quality Analytics

Objetivo: análisis detallado del rendimiento de conductores y la calidad de viajes. Es intencionadamente el panel más lento: consulta RAW.TRIPS_RAW directamente mediante acceso VARIANT. Anota aquí el tiempo como referencia para el benchmark de rendimiento de ClickHouse de la Parte 2.

Crear el panel:

  1. Dashboards → + Dashboard
  2. Título: Driver & Quality Analytics
  3. Actualización automática: 1 hora

Gráfico 11: viajes por valoración del conductor (barras)

  • Tipo de gráfico: Bar Chart
  • Conjunto: dqa_rating_dist
  • Eje X: rating_bucket
  • Métricas: SUM(trip_count)
  • Título: Trip Count by Driver Rating
  • Nota: recorre RAW.TRIPS_RAW con acceso VARIANT; compara el tiempo con ClickHouse

Gráfico 12: tarifa media por valoración (líneas)

  • Tipo de gráfico: Line Chart
  • Conjunto: dqa_rating_dist
  • Eje X: rating_bucket
  • Métricas: MAX(avg_fare)
  • Título: Average Fare by Driver Rating

Gráfico 13: ingresos por vehículo (barras horizontales)

  • Tipo de gráfico: Bar Chart (horizontal)
  • Conjunto: dqa_vehicle
  • Eje X: vehicle_type
  • Métricas: SUM(total_revenue), SUM(trip_count) (eje secundario)
  • Título: Revenue and Trip Count by Vehicle Type

Gráfico 14: impacto del nivel de tráfico (barras)

  • Tipo de gráfico: Bar Chart
  • Conjunto: dqa_traffic
  • Eje X: traffic_level
  • Métricas: MAX(avg_duration_minutes), MAX(avg_distance_miles) (eje secundario)
  • Título: Average Trip Duration and Distance by Traffic Level

Gráfico 15: viajes diarios por plataforma (líneas)

  • Tipo de gráfico: Line Chart
  • Conjunto: dqa_platform
  • Eje X: trip_date
  • Métricas: SUM(trip_count)
  • Series: app_platform
  • Título: Daily Trips by App Platform — Last 30 Days

Gráfico 16: tarifa dinámica por plataforma (tabla)

  • Tipo de gráfico: Table
  • Conjunto: dqa_platform
  • Columnas: app_platform, SUM(trip_count), AVG(avg_surge)
  • Límite de filas: 10
  • Título: Surge by Platform

Disposición:

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

Paso 4: verificar

  1. Abre cada panel y confirma que todos los gráficos se carguen sin errores
  2. Para el Panel 3, anota el tiempo de ejecución de la consulta dqa_rating_dist en Snowflake UI → Activity → Query History y guárdalo como benchmark de la migración

Gráfico de Superset con la distribución de valoraciones de conductores en el conjunto de Snowflake, concentradas entre 4,0 y 5,0

Paso 5: exportar para reutilizar

Cuando los paneles estén completos, expórtalos para que futuras ejecuciones puedan importarlos automáticamente:

  1. Abre cada panel → ··· → Export (se guarda como .zip)
  2. Coloca los archivos en superset/dashboards/:
    • 01_operations_command_center.zip
    • 02_executive_weekly_report.zip
    • 03_driver_quality_analytics.zip
  3. Vuelve a ejecutar ./init_superset.sh; los importará automáticamente en futuras configuraciones

Importante: credenciales de marcador en los ZIP incluidos.

Cada *.zip incluido tiene anonimizada su conexión en databases/*.yaml:

sqlalchemy_uri: snowflake://LAB_USER:XXXXXXXXXX@MYORG-MYACCOUNT/NYC_TAXI_DB/ANALYTICS?role=ANALYST_ROLE&warehouse=ANALYTICS_WH
  • Importación automática (./init_superset.sh): funciona sin cambios. El script registra la conexión real de Snowflake desde .env antes de importar y vuelve a aplicar el URI correcto después de cada importación (consulta _update_db en init_superset.sh), por lo que reemplaza los valores de marcador por tus credenciales.
  • Importación manual en la interfaz de Superset: la base importada se crea con el URI de marcador y no se conecta. Después de importar, ve a Settings → Database Connections → Edit y sustituye sqlalchemy_uri por tu URI real de Snowflake (por ejemplo, snowflake://<USER>:<PASSWORD>@<ORG>-<ACCOUNT>/NYC_TAXI_DB/ANALYTICS?role=ANALYST_ROLE&warehouse=ANALYTICS_WH).
  • Volver a exportar tus paneles: Superset incorpora tu localizador de cuenta y tu usuario en databases/*.yaml. Antes de incluir los ZIP reexportados, anonimízalos de nuevo como MYORG-MYACCOUNT / LAB_USER para no filtrar identificadores al historial de git.

En esta página

Paso 1: iniciar Superset y conectarlo a SnowflakeIniciar SupersetRegistrar la conexión a la base de datos (automático)Registrar la conexión a la base de datos (alternativa manual)Paso 2: crear todos los conjuntos de datosConjuntos de datos del panel 1ops_hourly_revenue: ingresos horarios por distritoops_zone_agg: agregados por zonaops_payment_split: distribución por tipo de pagoConjuntos de datos del panel 2exec_rolling_avg: medias móviles de 7 díasexec_top_trips: los 10 viajes de mayor valor por distritoexec_surge: impacto de las tarifas dinámicasConjuntos de datos del panel 3dqa_rating_dist: distribución de valoraciones de conductoresdqa_vehicle: ingresos por tipo de vehículodqa_traffic: nivel de tráfico frente a duración del viajedqa_platform: tendencias por plataforma de la aplicaciónPaso 3: crear los panelesPanel 1: Operations Command CenterGráfico 1: viajes por hora (líneas)Gráfico 2: ingresos por distrito (barras)Gráfico 3: distribución por tipo de pago (circular)Gráfico 4: viajes totales (número grande)Gráfico 5: resumen de rendimiento por distrito (tabla)Panel 2: Executive Weekly ReportGráfico 6: tendencia móvil de ingresos de 7 días (líneas)Gráfico 7a: volumen diario de viajes (número grande)Gráfico 7b: distancia media móvil de 7 días (líneas)Gráfico 8: 10 viajes principales por distrito (tabla)Gráfico 9: desglose de tarifas dinámicas (gráfico mixto)Gráfico 10: distribución de tarifas dinámicas (circular)Panel 3: Driver & Quality AnalyticsGráfico 11: viajes por valoración del conductor (barras)Gráfico 12: tarifa media por valoración (líneas)Gráfico 13: ingresos por vehículo (barras horizontales)Gráfico 14: impacto del nivel de tráfico (barras)Gráfico 15: viajes diarios por plataforma (líneas)Gráfico 16: tarifa dinámica por plataforma (tabla)Paso 4: verificarPaso 5: exportar para reutilizar
ES