Data Warehouses y Data Lakes

"Si tu warehouse es un montón de tablas crudas igual que producción, no es un warehouse — es un Frankenstein."

Qué vas a aprender en este capítulo

Un data warehouse no es solo "una BD grande". Tiene una estructura específica diseñada para análisis: tablas de hechos, dimensiones, modelado dimensional. Este capítulo cubre cómo diseñarlo, llenarlo (ETL/ELT) y consultarlo eficientemente.


3.1 Modelado dimensional — Kimball

💡 Intuición

Para análisis, no querés normalizar a 3FN como en OLTP. Querés desnormalizar para que las queries sean rápidas y comprensibles para analistas no técnicos.

Ralph Kimball (años 90) propuso el modelado dimensional: organizá los datos en torno a "hechos" (lo que pasó) y "dimensiones" (el contexto del hecho).

📐 Fundamento

Star schema:

                      [dim_tiempo]
                            │
                            │
[dim_cliente] ─────  [fact_ventas]  ───── [dim_platillo]
                            │
                            │
                      [dim_local]
  • Tabla de hechos (fact): mediciones numéricas (ventas, clicks, eventos). Una fila por evento.
  • Tablas de dimensión: descripciones (cliente, producto, tiempo, ubicación). Una fila por entidad.

Ejemplo La Esquina:

-- DIMENSIONES

CREATE TABLE dim_tiempo (
  tiempo_id        INT PRIMARY KEY,
  fecha            DATE,
  dia_semana       VARCHAR,
  mes              INT,
  trimestre        INT,
  año              INT,
  es_feriado       BOOLEAN
);

CREATE TABLE dim_platillo (
  platillo_id      INT PRIMARY KEY,
  nombre           VARCHAR,
  categoria        VARCHAR,
  precio_actual    DECIMAL,
  vegano           BOOLEAN
);

CREATE TABLE dim_cliente (
  cliente_id       INT PRIMARY KEY,
  nombre           VARCHAR,
  ciudad           VARCHAR,
  segmento         VARCHAR,  -- 'frecuente', 'ocasional', 'nuevo'
  fecha_registro   DATE
);

CREATE TABLE dim_local (
  local_id         INT PRIMARY KEY,
  nombre           VARCHAR,
  ciudad           VARCHAR,
  region           VARCHAR
);

-- TABLA DE HECHOS

CREATE TABLE fact_ventas (
  venta_id         BIGINT PRIMARY KEY,
  tiempo_id        INT REFERENCES dim_tiempo,
  platillo_id      INT REFERENCES dim_platillo,
  cliente_id       INT REFERENCES dim_cliente,
  local_id         INT REFERENCES dim_local,
  
  -- Medidas (lo que se agrega)
  cantidad         INT,
  precio_unitario  DECIMAL,
  total            DECIMAL,
  descuento        DECIMAL
);

Query analítico clásico (ventas por categoría y mes):

SELECT
  t.año, t.mes,
  p.categoria,
  SUM(f.total) AS ingresos,
  COUNT(*) AS num_ventas
FROM fact_ventas f
  JOIN dim_tiempo t ON f.tiempo_id = t.tiempo_id
  JOIN dim_platillo p ON f.platillo_id = p.platillo_id
WHERE t.año = 2026
GROUP BY t.año, t.mes, p.categoria
ORDER BY t.mes, ingresos DESC;

Snowflake schema: dimensiones también normalizadas (dim_platillo → dim_categoria). Más normalizado, menos denormalizado, más joins. Generalmente star schema es preferible para warehouses.

Slowly Changing Dimensions (SCD):

Las dimensiones cambian con el tiempo (un cliente cambia de ciudad, un platillo cambia de categoría). ¿Cómo manejarlo?

Tipo Descripción
SCD Type 1 Sobrescribir (perdés historia)
SCD Type 2 Agregar fila nueva con valid_from / valid_to (mantenés historia)
SCD Type 3 Agregar columna valor_anterior (historia limitada)
-- SCD Type 2 ejemplo
CREATE TABLE dim_cliente (
  surrogate_key    BIGSERIAL PRIMARY KEY,  -- nuevo id por cada versión
  cliente_id       INT,                     -- id de negocio (persistente)
  nombre           VARCHAR,
  ciudad           VARCHAR,
  segmento         VARCHAR,
  valid_from       DATE,
  valid_to         DATE,                    -- NULL si es la versión actual
  is_current       BOOLEAN
);

-- Cuando Ana cambia de ciudad:
UPDATE dim_cliente SET valid_to = '2026-05-01', is_current = false
WHERE cliente_id = 1 AND is_current = true;

INSERT INTO dim_cliente (cliente_id, nombre, ciudad, valid_from, is_current)
VALUES (1, 'Ana García', 'San Salvador', '2026-05-01', true);

3.2 ETL vs ELT

📐 Fundamento

ETL (Extract, Transform, Load):

[Source DB] → Extract → Transform (en servidor ETL) → Load → [Warehouse]
                              ↑
                       código complejo aquí

Modelo tradicional. Las transformaciones se hacen ANTES de cargar al warehouse.

ELT (Extract, Load, Transform) — moderno:

[Source DB] → Extract → Load (raw) → [Warehouse] → Transform (con SQL)
                                            ↑
                                  el warehouse tiene poder
                                  de cómputo gigante

Las transformaciones se hacen DENTRO del warehouse usando SQL. Aprovecha los warehouses modernos columnar (BigQuery, Snowflake) que son extremadamente rápidos.

ETL ELT
Transformaciones Antes de cargar Después de cargar
Lenguaje Java, Python SQL
Herramienta Informatica, Talend, Spark dbt
Escalabilidad Limitada por servidor ETL Limitada por warehouse
Flexibilidad Cambios requieren reprocesar Datos crudos siempre disponibles
Recomendado para Legacy, datos sensibles que no pueden ir crudos Stack moderno

dbt — el estándar actual de transformaciones:

-- models/staging/stg_pedidos.sql
SELECT
    id AS pedido_id,
    cliente_id,
    LOWER(estado) AS estado,
    total::DECIMAL AS total,
    fecha::TIMESTAMP AS creado_en
FROM {{ source('app_db', 'pedidos') }}
WHERE estado != 'TEST'

-- models/marts/fact_ventas.sql
SELECT
    p.pedido_id,
    p.cliente_id,
    ip.platillo_id,
    DATE_TRUNC('day', p.creado_en)::DATE AS fecha,
    SUM(ip.cantidad * ip.precio_unit) AS total
FROM {{ ref('stg_pedidos') }} p
JOIN {{ ref('stg_items_pedido') }} ip USING (pedido_id)
WHERE p.estado = 'CERRADO'
GROUP BY 1, 2, 3, 4
dbt run    # ejecuta todas las transformaciones
dbt test   # corre tests de calidad
dbt docs generate && dbt docs serve  # documentación auto-generada

dbt aporta:

  • Modularidad y reutilización ({{ ref(...) }}).
  • Tests de calidad de datos.
  • Documentación auto-generada.
  • Lineage (qué tabla viene de qué).
  • Versionado en Git.

3.3 Plataformas de Data Warehouse

📐 Fundamento

Comparación de los 3 grandes:

BigQuery Snowflake Redshift
Cloud GCP (multi via Omni) Multi-cloud AWS
Pricing Por bytes scaneados ($5/TB) Por créditos de cómputo + storage Por nodo (cluster)
Separación storage/compute Limitada
SQL flavor Standard SQL Snowflake SQL PostgreSQL-like
Serverless Sí (con auto-suspend) Serverless option
Time travel 7 días 1-90 días Snapshots

BigQuery — ejemplo de query a escala:

-- Esto consulta el dataset público de Wikipedia (TBs de datos) en segundos
SELECT
    title,
    SUM(views) AS total_views
FROM `bigquery-public-data.wikipedia.pageviews_2025`
WHERE DATE(datehour) BETWEEN '2025-01-01' AND '2025-12-31'
GROUP BY title
ORDER BY total_views DESC
LIMIT 100;

-- BigQuery escanea solo las particiones necesarias y procesa en paralelo
-- Costo: $5/TB scaneado. Tiempo: ~10 segundos para terabytes.

Optimizaciones específicas:

-- Particionamiento (por fecha)
CREATE TABLE pedidos
PARTITION BY DATE(fecha)
CLUSTER BY cliente_id, local_id     -- ordena dentro de la partición
AS
SELECT * FROM raw_pedidos;

-- Query: BigQuery lee SOLO las particiones de mayo 2026
SELECT COUNT(*) FROM pedidos
WHERE DATE(fecha) BETWEEN '2026-05-01' AND '2026-05-31';

Costos:

  • Sin particionamiento: query escanea toda la tabla = caro.
  • Con particionamiento: escanea solo lo necesario = barato.
  • Materializar resultados frecuentes en tablas separadas.
  • Usar slot reservations para cargas constantes (vs on-demand).

3.4 Data Lakes y Lakehouse

📐 Fundamento

Data Lake con S3:

s3://la-esquina-lake/
├── bronze/                # raw, como llega
│   ├── pedidos/
│   │   └── año=2026/mes=05/dia=05/file_001.json
│   └── eventos_app/
├── silver/                # limpio, deduplicado, tipado
│   └── pedidos/
│       └── año=2026/mes=05/parte-001.parquet
└── gold/                  # agregado, listo para BI
    ├── ventas_diarias.parquet
    └── ltv_clientes.parquet

Esta arquitectura "medallón" (bronze → silver → gold) es el patrón estándar.

Delta Lake / Iceberg / Hudi — formatos transaccionales:

Convierten S3 (que solo soporta archivos) en algo más parecido a una BD: ACID, time travel, schema evolution.

# Delta Lake con PySpark
df.write.format("delta").save("s3://lake/silver/pedidos/")

# Update (no soportado en Parquet plano, sí en Delta)
deltaTable.update(
    condition = "estado = 'PENDIENTE' AND creado_en < current_timestamp() - interval 24 hours",
    set = { "estado": "'EXPIRADO'" }
)

# Time travel
df_ayer = spark.read.format("delta") \
    .option("timestampAsOf", "2026-05-04") \
    .load("s3://lake/silver/pedidos/")

Lakehouse (Databricks):

Combina lake (storage barato S3) + warehouse (queries rápidas, transacciones). Una sola plataforma para BI, ML, streaming.

Cuándo Lake vs Warehouse:

Necesidad Mejor en
Análisis SQL ad-hoc rápido Warehouse (BigQuery, Snowflake)
ML training (datos crudos, mucha variedad) Lake
Reportes BI de baja latencia Warehouse
Logs históricos de bajo costo Lake
Compliance de datos crudos Lake

🛠️ En la práctica

La Esquina Cloud — pipeline completo de datos:

Day 0:
[App] → escribe en PostgreSQL (operativo)

Day 0 (continuo):
[Debezium CDC] captura cambios → publica en Kafka
[Spark Streaming] consume Kafka → escribe en bronze (S3 + Delta Lake)

Cada hora:
[Airflow DAG] dispara Spark job:
  - bronze → silver (limpieza, tipado, dedupe)
  - silver → gold (agregaciones para BI)

Cada hora:
[dbt] transforma datos en BigQuery:
  - Carga incremental desde Cloud Storage
  - Aplica modelado dimensional (fact + dim tables)
  - Corre tests de calidad

Diario:
[Dashboards Metabase/Looker] consultan BigQuery
[Notebooks ML] consumen gold layer en S3 para entrenar modelos

3.5 Ejercicios

✏️ Ejercicio 3.1 — Diseñar star schema

Diseñá un star schema para analizar el desempeño de delivery de La Esquina Cloud:

Métricas a analizar:

  • Tiempo promedio de entrega por zona/repartidor/día.
  • Tasa de cancelación por causa.
  • Ingresos por delivery por hora del día.

Definí: tabla(s) de hechos, dimensiones, y al menos una query analítica.


3.6 Para profundizar


Definiciones nuevas: modelado dimensional, star schema, snowflake schema, fact table, dimension table, SCD, ETL, ELT, dbt, BigQuery, Snowflake, Redshift, Delta Lake, Iceberg, Hudi, Lakehouse, arquitectura medallón (bronze/silver/gold), surrogate key.