Microsoft Fabric — Lakehouse vs Warehouse


Video explicativo

Si ya creaste un Lakehouse y un Warehouse en Fabric, es muy probable que hayas pensado lo mismo que cualquiera al abrirlos: se parecen demasiado. Los dos guardan tablas, los dos se consultan con SQL, y si los miras por dentro hasta tienen las mismas carpetas. Entonces, ¿por qué existen los dos? ¿Cuándo se elige uno y cuándo el otro?

Este post responde eso construyendo los dos con datos reales, aplicándoles el mismo SQL y viendo con nuestros ojos dónde uno dice que sí y el otro dice que no.

Los datos de este tutorial son reales. En vez de una fuente de ejemplo, vamos a trabajar con datos reales y públicos: la lista de videos y las métricas de un canal de YouTube auténtico, publicadas como archivos CSV que cualquiera puede abrir y descargar. Son dos tablas relacionadas por video_id:

  • videos — una fila por video (título, fecha de publicación, duración…). Es la dimensión.
  • video_metrics — vistas, likes y comentarios de cada video en distintas fechas (snapshots). Es la tabla de hechos.

Esa relación entre ambas tablas es justo la que nos mostrará para qué sirve de verdad el Warehouse. Las dos fuentes están aquí:

https://raw.githubusercontent.com/angelgarciadatablog/postgresql-youtube-dataset/main/csv/videos.csv
https://raw.githubusercontent.com/angelgarciadatablog/postgresql-youtube-dataset/main/csv/video_metrics.csv

1. Primero el Lakehouse: donde aterrizan los datos crudos

1.1 Qué es (y por qué empezamos por él)

El Lakehouse es la zona de aterrizaje de un proyecto de datos: el lugar donde el dato llega tal cual viene de la fuente, sin cocinar. Combina un data lake (archivos crudos de cualquier formato) con tablas Delta consultables con SQL. Empezamos por él porque es el primer sitio al que llegan nuestras dos fuentes.

1.2 Crear el Lakehouse

Desde el workspace → Nuevo elemento → Lakehouse. Le ponemos lakehouse_datablog_1 (el nombre debe empezar por letra y solo admite alfanuméricos y guiones bajos — sin espacios ni guiones medios). Al crearlo queda vacío, con sus dos secciones: Files (archivos crudos) y Tables (tablas Delta).

1.3 Ingestar las dos fuentes con un Flujo de datos Gen2

Los CSV viven en una URL, así que los traemos con un Flujo de datos Gen2 (la herramienta de ingesta de Fabric basada en Power Query, que corre en el trial F4).

  1. Con el Lakehouse abierto → Obtener datos → Nuevo flujo de datos Gen2. Nómbralo dataflow-datablog_1.
  2. Dentro del editor, primera fuente: Obtener datos → Web y pega la URL raw de videos.csv. Autenticación: Anónima (el repo es público). Power Query detecta que es CSV y lo separa en columnas.
  3. Renombra esa consulta a videos (clic derecho sobre la consulta → Cambiar nombre). Ese será el nombre de la tabla en el Lakehouse.
  4. Repite para la segunda fuente en el mismo flujo: Obtener datos → Web con la URL raw de video_metrics.csv, y renombra la consulta a video_metrics.
  5. Revisa los tipos en la vista previa: snapshot_date y published_at como Date/DateTime, y view_count, like_count, comment_count, duration_seconds como Whole Number (a veces entran como texto).
  6. Destino: como creaste el flujo desde el Lakehouse, este ya viene como destino predeterminado para ambas consultas. Deja el modo en Reemplazar.
  7. Guardar y ejecutar. En segundos las dos tablas aparecen bajo Tables en lakehouse_datablog_1.

⚠️ La URL debe ser la raw (raw.githubusercontent.com/...), no la vista blob de GitHub — esa devuelve la página HTML, no el CSV. Y el conector es Web, no "Text/CSV" (ese es para archivos locales o en Azure Storage).

1.4 El punto de conexión SQL del Lakehouse

Con las tablas dentro, el Lakehouse ya se puede consultar con SQL desde su Punto de conexión de análisis SQL (el desplegable de arriba a la derecha). Prueba una consulta con agregación — cuántos videos se publicaron por año y su duración promedio:

SELECT
    YEAR(published_at)    AS anio,
    COUNT(*)              AS videos_publicados,
    AVG(duration_seconds) AS duracion_promedio_seg
FROM videos
GROUP BY YEAR(published_at)
ORDER BY anio;

Funciona: el endpoint agrupa, cuenta y promedia sin problema.

Y como es de solo lectura pero no de solo-una-tabla, también puede unir las dos tablas en la misma consulta. Vamos por lo mismo —agrupar por año— pero sumando las métricas de video_metrics. Esa tabla tiene varios snapshots por video, así que primero nos quedamos con el último snapshot de cada video (para no contar las vistas repetidas) y luego agrupamos:

WITH ultimo_snapshot AS (
    SELECT
        video_id,
        view_count,
        like_count,
        ROW_NUMBER() OVER (PARTITION BY video_id ORDER BY snapshot_date DESC) AS rn
    FROM video_metrics
)
SELECT
    YEAR(v.published_at) AS anio,
    COUNT(*)             AS videos,
    SUM(m.view_count)    AS vistas_totales,
    SUM(m.like_count)    AS likes_totales
FROM videos AS v
JOIN ultimo_snapshot AS m
     ON v.video_id = m.video_id AND m.rn = 1
GROUP BY YEAR(v.published_at)
ORDER BY anio;

El JOIN corre sin problema: unir tablas para consultarlas es una lectura, y de eso el endpoint del Lakehouse sabe.

Guarda mentalmente este detalle — el Lakehouse se consulta con SQL, incluso uniendo tablas. En un momento veremos qué pasa cuando en vez de consultar queremos escribir, y ahí aparece la primera sorpresa.

2. Ahora el Warehouse: una base SQL completa

2.1 Qué es (y qué ventajas trae)

El Warehouse es una base de datos analítica T-SQL con todas las de la ley: no solo consulta, también escribe. Soporta INSERT, UPDATE, DELETE, CREATE TABLE, procedimientos almacenados, funciones y transacciones. Si vienes de SQL Server, es territorio conocido: se comporta como una base SQL completa, no como un simple visor de tablas.

2.2 Crear el Warehouse

Desde el workspace → Nuevo elemento → Warehouse. Lo llamamos warehouse_datablog_1. A diferencia del Lakehouse, no tiene sección de "Files": es SQL de nacimiento, y su explorador es el de una base de datos (esquemas, tablas, vistas, procedimientos). Nace vacío, listo para que le escribamos con SQL.

3. El Warehouse en acción: materializar y servir

Ya tenemos los dos elementos. Ahora el Warehouse hace lo que el Lakehouse no puede: escribir — y con eso convierte una consulta en una tabla lista para el reporte.

3.1 Materializar la tabla del query anterior

En la sección 1.4 corrimos la consulta del JOIN + GROUP BY en el endpoint del Lakehouse y funcionó, porque un SELECT es una lectura. Pero ahí está el límite: el endpoint del Lakehouse ejecuta la consulta pero no puede guardarla como tabla. Y esa es la respuesta a la pregunta con que abre el post — por debajo los dos guardan lo mismo (tablas Delta en OneLake); la única diferencia real es quién tiene permiso de escribir. El Warehouse sí escribe, así que es él quien materializa.

Antes de escribir la tabla hay que darle al Warehouse visibilidad del Lakehouse. No hay conexión que configurar —los dos viven en el mismo OneLake—: en la Nueva consulta SQL del Warehouse, usa la acción + Warehouses del Explorador y agrega tu lakehouse_datablog_1 (ambos deben estar en el mismo workspace). Desde ahí lo referencias con un nombre de tres partes (lakehouse.esquema.tabla).

Ahora materializamos la consulta anterior con CREATE TABLE AS SELECT (CTAS):

CREATE TABLE dbo.metricas_por_anio AS
WITH ultimo_snapshot AS (
    SELECT
        video_id,
        view_count,
        like_count,
        ROW_NUMBER() OVER (PARTITION BY video_id ORDER BY snapshot_date DESC) AS rn
    FROM lakehouse_datablog.dbo.video_metrics
)
SELECT
    YEAR(v.published_at) AS anio,
    COUNT(*)             AS videos,
    SUM(m.view_count)    AS vistas_totales,
    SUM(m.like_count)    AS likes_totales
FROM lakehouse_datablog.dbo.videos AS v
JOIN ultimo_snapshot AS m
     ON v.video_id = m.video_id AND m.rn = 1
GROUP BY YEAR(v.published_at);

Dos ajustes respecto a la consulta de la sección 1.4: las tablas del Lakehouse van con nombre de tres partes, y se quita el ORDER BY final (una tabla no guarda orden — se ordena al consultarla). Con eso, metricas_por_anio queda como tabla física dentro del Warehouse.

3.2 Qué más puede el Warehouse

El CTAS es solo una de las cosas que un Warehouse —una base T-SQL completa— sabe hacer. Todas estas son escrituras que el endpoint del Lakehouse no permite:

  • INSERT INTO ... SELECT — cargar una tabla en lote a partir de otras (el INSERT que sí se usa en analítica; fila por fila a mano es raro).
  • UPDATE / DELETE — corregir o depurar datos ya cargados.
  • MERGE — actualización incremental (upsert): cuando llegan snapshots nuevos, actualiza lo existente e inserta lo nuevo de un golpe.
  • Procedimientos almacenados y funciones — encapsular la lógica de transformación para reutilizarla y programarla.
  • Transacciones (BEGIN TRAN / COMMIT / ROLLBACK) — varios pasos de forma atómica: o se aplican todos o ninguno.

(Automatizar el refresco de metricas_por_anio con un procedimiento + un Data Pipeline programado —el equivalente a una scheduled query de BigQuery— es material de un tema aparte de orquestación.)

3.3 Del Warehouse al reporte: modelo semántico y Power BI

Para visualizar en Power BI hace falta un modelo semántico, y aquí se cierra el porqué de haber materializado. El modo estrella de Fabric —Direct Lake, rápido y que se refresca solo— solo lee tablas Delta físicas, no vistas. Por eso materializamos con CTAS: una vista no serviría para Direct Lake (obligaría a Import, con refresco manual, o a DirectQuery, más lento).

Desde el Warehouse:

  1. Cinta → grupo ReportingNuevo modelo semántico.
  2. Nómbralo (ej. modelo-metricas-youtube), confirma el área de trabajo y selecciona la tabla metricas_por_anio → Confirmar.
  3. Como la tabla es Delta física en OneLake, el modelo nace en Direct Lake: cuando reconstruyas la tabla, el reporte ve el dato nuevo sin refresco manual.

Y el cierre, ya en Nuevo reporte: un gráfico de columnas con anio en el eje X y vistas_totales en el eje Y. En segundos ves qué año concentró más vistas. El pipeline completo, del CSV crudo al gráfico:

Lakehouse (crudo) → CTAS en Warehouse (tabla física) → modelo semántico Direct Lake → reporte

4. ¿Puedo saltarme el Lakehouse? Sí: la ingesta directa

Una duda natural si prefieres el Warehouse: ¿los datos están obligados a aterrizar primero en un Lakehouse? No. El patrón "Lakehouse primero" es una recomendación de arquitectura, no una imposición técnica. El Warehouse recibe ingesta directa por varias vías oficiales:

Vía Estilo Típico para
Flujo de datos Gen2 (destino: Almacén) Sin código, con transformaciones APIs, archivos, bases — el camino del analista
Data Pipeline (actividad de copia) Low-code, programable Cargas repetitivas y volumen
COPY INTO (T-SQL) Código, máximo rendimiento Archivos CSV/Parquet/JSONL en Azure Storage
CTAS / INSERT...SELECT / OPENROWSET Código Tablas del mismo workspace o archivos externos

Lo probamos en vivo: el mismo flujo de ingesta (CSV público de YouTube → Flujo de datos Gen2), pero eligiendo Almacén como destino en lugar del Lakehouse. Dos diferencias aparecieron en el camino:

  1. La conexión se vuelve explícita. Al declarar el destino a mano, Gen2 pide crear una conexión con credencial (tipo "Cuenta de organización"). Cuando el destino era el Lakehouse "predeterminado", ese paso existía igual — solo que resuelto en silencio.
  2. El resultado es idéntico en experiencia: al terminar la ejecución, la tabla apareció en Tables/dbo del Warehouse, lista para consultarse — esta vez sin nombre de tres partes, porque es tabla local.

¿Y entonces por qué el patrón Lakehouse-primero sigue siendo el default profesional? Tres razones:

  1. Conservar el crudo. El Lakehouse guarda el dato tal cual llegó. Si mañana cambia la transformación, reprocesas desde el crudo sin volver a pegarle a la fuente. El Warehouse solo guarda el resultado cocinado.
  2. Fuentes sucias o diversas. JSON anidados, archivos irregulares, volúmenes que piden Spark — todo eso aterriza natural en un Lakehouse. Al Warehouse le gusta recibir datos ya estructurados.
  3. Un aterrizaje, muchos consumidores. El crudo del Lakehouse alimenta al Warehouse, a notebooks y a otros flujos sin repetir la ingesta.

Para una fuente limpia y estructurada, la ruta corta fuente → Gen2 → Warehouse → modelo semántico → reporte es perfectamente válida.

5. Otras diferencias visibles (una vez que sabes qué mirar)

  1. El selector de caras. El Lakehouse tiene un desplegable para alternar entre su vista de Lakehouse y su endpoint SQL — porque son dos caras del mismo elemento. El Warehouse no tiene selector: él es SQL de nacimiento.
  2. El explorador. El panel del Warehouse es el de una base de datos: esquemas, tablas, vistas, procedimientos, funciones. El del Lakehouse es un explorador de contenido: carpetas con archivos crudos (que puedes subir a mano: CSV, JSON, imágenes) y tablas. Al Warehouse no le subes archivos por la interfaz — todo le entra por SQL o por pipelines.
  3. Los esquemas. El Warehouse organiza sus tablas en esquemas (dbo, y los que crees) desde el primer día, como cualquier base SQL. En el Lakehouse los esquemas son opcionales.

6. Entonces, ¿cuál elijo? Los tres caminos

La pregunta decisiva es: ¿quién va a escribir tus tablas y en qué lenguaje piensa tu equipo? Con las piezas que ya probamos, existen tres arquitecturas posibles — las tres válidas, para momentos distintos:

CAMINO 1 — Lakehouse-céntrico
Fuente → Gen2 → Lakehouse → modelo semántico → reporte

CAMINO 2 — Warehouse-céntrico
Fuente → Gen2 → Warehouse (raw) → CTAS/procedimientos (tablas cocinadas)
                                    → modelo semántico → reporte

CAMINO 3 — Los dos juntos (el patrón enterprise)
Fuente → Gen2 → LAKEHOUSE (aterriza el crudo, tal cual llegó)
                    ↓  consulta de tres partes (CTAS — sin copiar nada)
                WAREHOUSE (staging → dimensiones → hechos, en T-SQL)
                    ↓
                modelo semántico → reporte

6.1 Camino Lakehouse

Elígelo si tus datos llegan como archivos o desde APIs, los trae un Flujo de datos Gen2 o los procesa Spark, y el SQL lo usas para consultar y verificar. Es el punto de aterrizaje natural de un proyecto analítico — y por eso es la primera pieza que se aprende.

6.2 Camino Warehouse

Elígelo si tu equipo construye el modelo analítico escribiendo T-SQL: la raw llega directo al Warehouse (ingesta directa de la sección 4) y las tablas para el reporte se cocinan con CTAS y procedimientos. Si vienes de SQL Server y tus fuentes son limpias y estructuradas, este camino es legítimo y cómodo.

6.3 El tercer camino: los dos juntos

El patrón profesional por excelencia. Cada casa hace lo que mejor sabe:

  1. El Lakehouse es la zona de aterrizaje: guarda el crudo intacto (reprocesable para siempre), acepta cualquier formato, y si un día llega volumen salvaje, Spark trabaja ahí. Aquí aterrizan nuestras videos y video_metrics sin tocar.
  2. El Warehouse es la fábrica: toma el crudo del Lakehouse con nombres de tres partes y lo convierte en modelo analítico con T-SQL — como hicimos al materializar metricas_por_anio.
  3. El modelo semántico sirve la mesa desde las tablas cocinadas del Warehouse.

Este patrón tiene nombre en la industria: arquitectura medallón (medallion) — capas bronce (crudo), plata (limpio) y oro (modelado para consumo). En Fabric se mapea natural: bronce en el Lakehouse (videos, video_metrics crudas), plata y oro en el Warehouse (metricas_por_anio, lista para el reporte).

6.4 Los tres caminos frente a frente

Camino Lakehouse Camino Warehouse Los dos juntos
La raw llega a Lakehouse (tabla o archivo) Warehouse (tabla) Lakehouse (crudo intacto)
Transformas con Otro Gen2, o Spark T-SQL: CTAS, procedimientos T-SQL, leyendo el crudo con tres partes
¿Conservas el crudo original? No (solo lo cocinado)
El modelo semántico lee Tablas del Lakehouse Tablas cocinadas del Warehouse Tablas oro del Warehouse
Ideal para Fuentes crudas o diversas, reprocesos, Spark Analista SQL con fuentes limpias Proyectos que crecen: varias fuentes, reproceso histórico, equipos por capa

¿Cuándo se justifica la dupla frente a un camino simple? Cuando el proyecto crece: varias fuentes, necesidad de reprocesar el histórico, equipos distintos tocando capas distintas. Para un proyecto personal de una sola fuente, cualquiera de los dos caminos simples basta — la dupla es el traje para cuando la cosa se pone seria.

Y las otras dos casas del mapa, para no perderlas de vista: si lo que necesitas es que una aplicación registre operaciones en vivo (muchas escrituras pequeñas y simultáneas), eso no es ninguno de estos dos — es una SQL Database (OLTP). Y si tu dato es un flujo continuo de eventos (logs, sensores, clics), su casa es el Eventhouse con KQL. Cada una merece su propia exploración.

7. La tabla de decisión

Lakehouse Warehouse
Almacenamiento físico Delta en OneLake Delta en OneLake (idéntico)
Quién escribe Spark, Flujos Gen2 Motor T-SQL
SQL de escritura de datos (INSERT/UPDATE/DELETE/MERGE) ❌ (endpoint solo lectura)
Materializar una tabla nueva (CTAS, datos físicos)
Transacciones que modifican datos
Vistas, funciones y procedimientos (envuelven un SELECT) ✅ (el endpoint SQL los soporta)
Archivos crudos (CSV, JSON, imágenes) ✅ (sección Files) ❌ por interfaz
Lee tablas del otro ✅ (vía Spark/shortcuts) ✅ (nombre de tres partes)
Ingesta directa desde fuentes (Gen2, pipelines)
Perfil natural Analista/ingeniero de datos Desarrollador SQL
Rol en el patrón clásico Aterrizar y preparar Modelar y servir

La línea de fondo (más honesta que la tabla): el almacenamiento es idéntico y los dos materializan tablas Delta. Lo único que de verdad los separa es en qué lenguaje transformas: en T-SQL (CTAS, procedimientos, MERGE) → Warehouse; en Power Query (Gen2) o Spark → Lakehouse. Por eso, para un proyecto simple de una sola fuente puede que ni necesites un Warehouse — el Lakehouse con Gen2 te alcanza. El Warehouse gana cuando piensas y trabajas en SQL (por ejemplo, si vienes de BigQuery o SQL Server): es el elemento que te deja construir y materializar con el lenguaje que ya dominas.

Gotchas

  • El endpoint SQL del Lakehouse "solo lectura" sí crea vistas, funciones y procedimientos — "solo lectura" se refiere a los datos: no puedes INSERT/UPDATE/DELETE ni materializar una tabla (CTAS). Pero los objetos que solo envuelven un SELECT —vistas, funciones escalares inlineables, procedimientos— sí se pueden crear en el endpoint (confirmado en la doc oficial). La línea real no es "crear objetos" sino "escribir datos". Lo único exclusivo del Warehouse es escribir/modificar datos y materializar tablas.
  • El nombre de tres partes exige el nombre exacto de la tabla — si la tabla del Lakehouse no existe con ese nombre, el Warehouse devuelve Invalid object name (error 208). Verificar el nombre real en el explorador del Lakehouse antes de culpar a la sintaxis: la tabla pudo haber sido renombrada después de crear el flujo. Y antes de nada, el Lakehouse debe estar agregado al Explorador con + Warehouses (sección 3.1).
  • El CTAS de Fabric acepta CTE pero no ORDER BY — un WITH ... AS (...) antes del SELECT funciona en el CTAS del Warehouse (verificado en F4), aunque la gramática de la doc oficial de Fabric no lo liste explícitamente. Lo que no admite es ORDER BY a nivel de query: una tabla no guarda orden, así que se ordena al consultar, no al materializar.
  • Los "Staging…ForDataflows" del selector + Warehouses son de sistema — al ejecutar un Flujo de datos Gen2, Fabric crea automáticamente elementos internos (StagingLakehouseForDataflows_<fecha>, StagingWarehouseForDataflows_<fecha>) como mesa de trabajo intermedia. Aparecen en el selector porque lista todos los endpoints SQL del workspace. No borrarlos, no consultarlos, no escribirlos: son plomería de Gen2. Elegir solo el Lakehouse propio.
  • El explorador del Warehouse no siempre se refresca solo — tras un CTAS, usar el botón Actualizar para ver la tabla nueva en el panel.
  • El CLI de Fabric (fab) no ejecuta T-SQL — crea el Warehouse y navega sus tablas, pero las consultas van por conexión SQL (editor del portal o cliente SQL con autenticación de Entra).
  • La URL de un CSV en GitHub debe ser la raw, no la de la página — la vista github.com/.../blob/... devuelve HTML, no el CSV. En Gen2 hay que usar raw.githubusercontent.com/.../... (cambiar github.com por raw.githubusercontent.com y quitar el /blob). Con la URL equivocada, el flujo trae la página web entera en vez de los datos.
  • Un CSV por URL entra por el conector Web, no por "Text/CSV" — el conector "Text/CSV" es para archivos locales o en Azure Storage. Para un archivo detrás de una URL se usa el conector Web (autenticación Anónima si el repo es público); Power Query detecta que el contenido es CSV y lo parsea en columnas solo.

Referencia oficial

  • Qué es un Lakehouse: https://learn.microsoft.com/en-us/fabric/data-engineering/lakehouse-overview
  • Qué es el Data Warehouse de Fabric: https://learn.microsoft.com/en-us/fabric/data-warehouse/data-warehousing
  • SQL analytics endpoint (solo lectura): https://learn.microsoft.com/en-us/fabric/data-warehouse/data-warehousing#sql-analytics-endpoint-of-the-lakehouse