Materialized Lake Views—A Real‑World Demo with F1 Data

This post demonstrates Materialized Lake Views (MLVs) in a realistic Microsoft Fabric setup. We pair MLVs with Real‑Time Intelligence (RTI)—specifically an Eventhouse (KQL database)—to show how event‑scale data (lap times and race results) can feed a medallion‑style model with no bespoke pipelines and clean governance.

We’ll use the Kaggle Formula 1 World Championship dataset (link shows 1950–2024) and build this end‑to‑end:

  • Eventhouse (RTI) to own lap_times and results, surfaced to OneLake as Delta via OneLake Availability.
  • Lakehouse to declare MLVs (Silver/Gold) over those Delta tables.
  • Warehouse to query Lakehouse tables with three‑part names (no replication).
  • Power BI Semantic Model in Direct Lake over the materialized Delta outputs.

1) Concept & dataset

Concept: Make MLVs the “engine” of your medallion layers. Keep high‑volume facts in Eventhouse, expose them to OneLake as Delta, and declare your Silver/Gold logic as MLVs in the Lakehouse. Query the results from a Warehouse and publish a Direct Lake model—without wiring separate pipelines or scattering logic across tools.

Dataset: Kaggle — Formula 1 World Championship (1950–2024). You’ll use:
drivers.csvconstructors.csvcircuits.csvraces.csvresults.csvlap_times.csv.


2) Reference architecture (MLV‑first)

Kaggle CSVs (drivers, constructors, circuits, races, results, lap_times)
        └──► Eventhouse (KQL DB): ingest CSVs as KQL tables
               └──► OneLake Availability: exposes Eventhouse tables as Delta

Lakehouse
  └──► Table Shortcuts to the Eventhouse-backed Delta tables
  └──► Materialized Lake Views (Silver/Gold) declared in Spark SQL

Warehouse (SQL)
  └──► Cross-database queries (database.schema.table) against Lakehouse Delta

Power BI
  └──► Direct Lake model bound to MLV Delta outputs (avoid SQL views to prevent fallback)

Why this pattern? Eventhouse is purpose‑built for event‑scale ingestion and ad‑hoc KQL. OneLake Availability turns those tables into Delta so every other engine can read them without copy steps. MLVskeep transformation logic declarative, governable, and monitorable.


3) Create Fabric items

  • Eventhouse (KQL DB): f1_event
  • Lakehouse: f1_lake (enable schemas)
  • Warehouse (SQL): f1_wh (optional but useful for SQL demos)

Note that when you create your lakehouse, you won’t see a default semantic model. This model has been sunset, and now you will need to create a semantic model manually, following best practice. This avoids a number of errors.


4) Load the dataset into Eventhouse (KQL)

Use the Eventhouse ingestion wizard or KQL commands. Below is a concise pattern you can adapt.

4.1 Create tables

// Event tables
.create table lap_times (
  raceId:int, driverId:int, lap:int, position:int, [time]:string, milliseconds:long
);

.create table results (
  resultId:int, raceId:int, driverId:int, constructorId:int,
  number:int, grid:int, position:string, positionText:string, positionOrder:int,
  points:real, laps:int, [time]:string, milliseconds:long,
  fastestLap:int, rank:int, fastestLapTime:string, fastestLapSpeed:real,
  statusId:int
);

// Reference tables
.create table drivers (
  driverId:int, driverRef:string, number:int, code:string,
  forename:string, surname:string, dob:string, nationality:string, url:string
);

.create table constructors (
  constructorId:int, constructorRef:string, name:string, nationality:string, url:string
);

.create table circuits (
  circuitId:int, circuitRef:string, name:string, location:string,
  country:string, lat:real, lng:real, alt:int, url:string
);

.create table races (
  raceId:int, year:int, round:int, circuitId:int,
  name:string, [date]:string, [time]:string, url:string
);

4.2 Define CSV mappings (example for one table; repeat as needed or use the wizard)

.create table results ingestion csv mapping "ResultsCsv"
'['
 '{"column":"resultId","datatype":"int","Ordinal":"0"},'
 '{"column":"raceId","datatype":"int","Ordinal":"1"},'
 '{"column":"driverId","datatype":"int","Ordinal":"2"},'
 '{"column":"constructorId","datatype":"int","Ordinal":"3"},'
 '{"column":"number","datatype":"int","Ordinal":"4"},'
 '{"column":"grid","datatype":"int","Ordinal":"5"},'
 '{"column":"position","datatype":"string","Ordinal":"6"},'
 '{"column":"positionText","datatype":"string","Ordinal":"7"},'
 '{"column":"positionOrder","datatype":"int","Ordinal":"8"},'
 '{"column":"points","datatype":"real","Ordinal":"9"},'
 '{"column":"laps","datatype":"int","Ordinal":"10"},'
 '{"column":"time","datatype":"string","Ordinal":"11"},'
 '{"column":"milliseconds","datatype":"long","Ordinal":"12"},'
 '{"column":"fastestLap","datatype":"int","Ordinal":"13"},'
 '{"column":"rank","datatype":"int","Ordinal":"14"},'
 '{"column":"fastestLapTime","datatype":"string","Ordinal":"15"},'
 '{"column":"fastestLapSpeed","datatype":"real","Ordinal":"16"},'
 '{"column":"statusId","datatype":"int","Ordinal":"17"}'
']';

4.3 Ingest from storage

My recommendation here is to use the wizard to ingest the data you need via a shortcut. (I pulled the data for the “changeable” items, like the results and standings from an Azure Blob Storage data source after I had truncated them to remove the 2024 data so that we can demonstrate ingesting new data through the eventhouse.

  1. Click on the ellipsis next to the desired table (…)
  2. Click “Get Data”
  3. Choose your storage method
  4. Fill out your connection details (making sure to properly filter your data to only a single file format)
  5. Validate your schema mappings
  6. Complete!

For completeness, I also created tables for the remaining reference tables: seasons and status, which is supported from this same interface.


5) Enable OneLake Availability on Eventhouse

Turn it on at the database or table level. Eventhouse will publish the KQL tables to OneLake as Delta with a governed latency window. While enabled, some operations (e.g., rename) are restricted. This makes your data instantly consumable by Lakehouse, Warehouse, and Power BI without extra copy steps.


6) In the Lakehouse, create Table Shortcuts to Eventhouse tables

Create new schemas, bronze, silver, and gold in the lakehouse.

From f1_lake → Tables → bronze ▸ New Shortcut → Source: KQL database → select driversconstructorscircuitsracesresultslap_times.
They appear as Delta tables in the Lakehouse—queryable by Spark, the SQL analytics endpoint, and Direct Lake.


7) Declare Materialized Lake Views (MLVs)

Prereqs: In the Lakehouse, schemas must be enabled. Create MLVs from a notebook using Spark SQL. You’ll monitor, schedule, and see lineage from the Managed materialized lake views pane.

7.1 Silver layer (standardize/enrich)

-- silver.dim_driver
CREATE MATERIALIZED LAKE VIEW IF NOT EXISTS silver.dim_driver AS
SELECT CAST(driverId AS INT)          AS driver_id,
       CONCAT(forename, ' ', surname) AS driver_name,
       code,
       nationality,
       CAST(dob AS DATE)              AS dob
FROM drivers;

-- silver.dim_constructor
CREATE MATERIALIZED LAKE VIEW IF NOT EXISTS silver.dim_constructor AS
SELECT CAST(constructorId AS INT) AS constructor_id,
       name AS constructor_name,
       nationality
FROM constructors;

-- silver.dim_circuit
CREATE MATERIALIZED LAKE VIEW IF NOT EXISTS silver.dim_circuit AS
SELECT CAST(circuitId AS INT) AS circuit_id,
       name AS circuit_name,
       location,
       country
FROM circuits;

-- silver.dim_race (join circuits)
CREATE MATERIALIZED LAKE VIEW IF NOT EXISTS silver.dim_race AS
SELECT r.raceId             AS race_id,
       r.year               AS season,
       r.round,
       r.name               AS race_name,
       CAST(r.date AS DATE) AS race_date,
       r.circuitId          AS circuit_id,
       c.circuit_name,
       c.location,
       c.country
FROM races r
LEFT JOIN silver.dim_circuit c
  ON r.circuitId = c.circuit_id;

-- silver.results_clean with data quality
CREATE MATERIALIZED LAKE VIEW IF NOT EXISTS silver.results_clean
(
  CONSTRAINT pos_order_valid CHECK (positionOrder >= 1) ON MISMATCH DROP,
  CONSTRAINT points_nonneg   CHECK (points >= 0)        ON MISMATCH FAIL
)
AS
SELECT resultId, raceId, driverId, constructorId,
       grid, positionOrder, points, laps,
       fastestLap, fastestLapTime, fastestLapSpeed
FROM results;

-- (Optional) silver.laps_clean
CREATE MATERIALIZED LAKE VIEW IF NOT EXISTS silver.laps_clean
(
  CONSTRAINT lap_ms_nonneg CHECK (milliseconds >= 0) ON MISMATCH DROP
)
AS
SELECT raceId, driverId, lap, position, milliseconds
FROM lap_times;

7.2 Gold layer (analytics‑ready facts)

-- gold.fact_results
CREATE MATERIALIZED LAKE VIEW IF NOT EXISTS gold.fact_results AS
SELECT d.driver_id,
       co.constructor_id,
       r.season,
       r.race_id,
       rc.grid,
       rc.positionOrder AS finish_pos,
       rc.points,
       rc.laps,
       rc.fastestLap,
       rc.fastestLapTime,
       rc.fastestLapSpeed
FROM silver.results_clean rc
JOIN silver.dim_race       r  ON rc.raceId       = r.race_id
JOIN silver.dim_driver     d  ON rc.driverId     = d.driver_id
JOIN silver.dim_constructor co ON rc.constructorId = co.constructor_id;

-- gold.fact_laps
CREATE MATERIALIZED LAKE VIEW IF NOT EXISTS gold.fact_laps AS
SELECT r.season,
       l.raceId       AS race_id,
       l.driverId     AS driver_id,
       l.lap,
       l.position     AS lap_position,
       l.milliseconds AS lap_time_ms
FROM silver.laps_clean l
JOIN silver.dim_race  r ON l.raceId = r.race_id;

Operate the MLVs: From Manage materialized lake views, set a schedule, trigger a manual refresh, review lineage, and inspect data‑quality results (dropped rows vs. failures). Current behavior is full refresh on change; runs are skipped when no inputs changed.


8) Query from the Warehouse (no copies)

From f1_wh, use three‑part names (database.schema.table) to query Lakehouse tables directly:

-- Inspect gold fact
SELECT TOP 10 *
FROM f1_lake.gold.fact_results
ORDER BY season DESC, race_id DESC;

-- Top winners
SELECT d.driver_name, COUNT(*) AS wins
FROM f1_lake.gold.fact_results fr
JOIN f1_lake.silver.dim_driver d
  ON fr.driver_id = d.driver_id
WHERE fr.finish_pos = 1
GROUP BY d.driver_name
ORDER BY wins DESC;

9) Build the Semantic Model (Power BI) in Direct Lake

Bind model tables directly to the Delta outputs (gold.*, selected silver.*). Avoid layering SQL views between Power BI and your Delta tables; views can cause Direct Lake → DirectQuery fallback. Use physical Delta tables (your MLV outputs) for the best performance.

Starter DAX

Wins := COUNTROWS( FILTER( 'fact_results', 'fact_results'[finish_pos] = 1 ) )
Podiums := COUNTROWS( FILTER( 'fact_results', 'fact_results'[finish_pos] <= 3 ) )
Avg Lap (ms) := AVERAGE('fact_laps'[lap_time_ms])
Points := SUM('fact_results'[points])

10) Notes & gotchas

  • MLVs today: created via Spark SQL; refresh is full on change with skipped runs when unchanged.
  • Eventhouse availability: while enabled, certain DDL operations (e.g., rename) are restricted; configure latency to balance freshness vs. cost.
  • Direct Lake: binding to tables (not SQL views) avoids fallback to DirectQuery.
  • Alternate path for reference data: If you prefer not to store dims in Eventhouse, you can import the CSVs into the Lakehouse as Delta (e.g., “Create table from files” or a short Spark notebook). This does create a managed copy, but keeps your MLV story intact—still no separate pipeline tool required.

11) Quick validation

KQL (Eventhouse)

results
| summarize total_points = sum(points) by driverId
| top 10 by total_points desc

Spark SQL (Lakehouse)

SELECT season, COUNT(DISTINCT race_id) AS races
FROM gold.fact_results
GROUP BY season
ORDER BY season DESC;

T‑SQL (Warehouse)

SELECT TOP 20 r.season, r.race_name, d.driver_name, fr.finish_pos, fr.points
FROM f1_lake.gold.fact_results AS fr
JOIN f1_lake.silver.dim_race   AS r ON fr.race_id   = r.race_id
JOIN f1_lake.silver.dim_driver AS d ON fr.driver_id = d.driver_id
ORDER BY r.season DESC, r.race_id DESC, fr.finish_pos ASC;

Why Materialized Lake Views (MLVs) matter

  • Declarative pipelines: one Spark SQL statement defines each transform. Fabric handles orchestration, lineage, and monitoring—no separate ETL/pipeline asset to wire up.
  • Governed & observable: MLVs show source dependencies, run history, and data‑quality outcomes in a single, first‑class UI.
  • Built‑in data quality: add CHECK constraints with ON MISMATCH DROP | FAIL to reject or fail on bad data—without custom code.
  • Predictable refresh: full refresh when sources change, skipped runs when they don’t—simple behavior that’s easy to explain to stakeholders.
  • Open downstream: MLV outputs are Delta tables in OneLake—query them from Warehouse (SQL), notebooks, and Power BI in Direct Lake for fast BI.

Closing

MLVs turn your medallion layers into governed assets: one statement per transform, first‑class lineage, schedulable refresh, and built‑in data quality. Pair them with Eventhouse and OneLake Availability to keep ingestion fast and analytics open—then light up Warehouse and Direct Lake without extra copy steps or pipeline sprawl.

Implementing Stars and Galaxies in Power BI

Power BI rewards clean dimensional models—but it also punishes sloppy ones. This post walks through how to implement star and galaxy schemas in Power BI semantic models, why ambiguous (multiple) filter paths cause headaches, why implicit measures don’t scale beyond the simplest star, and how tightly defined data products keep your BI ecosystem fast, correct, and governable. Because this is such an important topic, I’ve included links to references with each point.

Continue reading “Implementing Stars and Galaxies in Power BI”

Conway’s Law for Data Teams

Two Dashboards, One Truth

On Monday, Maya—head of a seven‑person data team—watched two dashboards disagree.

The executive dashboard showed $11.2M in MRR. Sales’ dashboard said $10.6M. Both pulled from “the warehouse.” Both refreshed nightly. Neither was “wrong”; they just measured different things.

Maya didn’t control how Sales Ops or Marketing were organized, who they reported to, or which tools they bought. She controlled only her data team—its models, interfaces, and operations. Yet the warehouse had clearly taken on the shape of the company’s communication patterns.

Conway’s Law, without asking permission, had moved in.

Continue reading “Conway’s Law for Data Teams”

A Practical Introduction to Star Schema Data Architecture

Dimensional modeling remains the most effective way to make analytics fast, understandable, and resilient. The star schema sits at the center of that approach: a simple, denormalized structure where fact tables record measurable events and dimension tables provide descriptive context. In this post, we’ll ground the core ideas, clarify the often‑confused concept of snowflaking (and when it’s worth it), and show how to scale from a single star to a galaxy schema (a.k.a. fact constellation) without losing your footing.

Continue reading “A Practical Introduction to Star Schema Data Architecture”

Foundational + Derived Data Products in a Data Mesh

data mesh is a sociotechnical approach to analytical data that decentralizes responsibility to business domains while standardizing the way data is produced and consumed. It’s grounded in four principles: domain ownership, data as a product, a self‑serve data platform, and federated governance. In practice, it asks each domain team to publish data as a product—discoverable, trustworthy, and operable—while a common platform automates cross‑cutting rules (access, lineage, quality, security).

Zhamak Dehghani frames a data product as an architectural quantum: the smallest independently deployable unit that bundles data, code, metadata, and policy, with a versioned contract and a clear interface (APIs or governed views). Treating both foundational and derived products as quanta is the key to decoupled evolution without breaking interoperability.

Continue reading “Foundational + Derived Data Products in a Data Mesh”

Saturday Film → Monday Growth: How you can use Microsoft Power Platform to level up your player grading experience.

It’s Saturday in the fieldhouse. You’re rolling through last night’s game with your staff. The goal isn’t to “get through the tape”—it’s to walk out with player‑by‑player statistics, clean per‑play grades, and a short list of reps each kid needs next week.

Here’s how to do it:

  1. A simple grading and stats workflow that every position coach can run while you watch film, and
  2. A practical Power Platform setup (Dataverse + model‑driven app + canvas app) that makes it quick to build and easy to maintain.
Continue reading “Saturday Film → Monday Growth: How you can use Microsoft Power Platform to level up your player grading experience.”

Data Products in Fabric, Part 3: Why Fabric Is Ideal, What to Expose, and How to Govern (with zero‑copy patterns)

In Parts 1–2, we framed a data product as a reusable, self‑contained package that bundles data, metadata, access methods, and governance to deliver an outcome—discoverable, interoperable, and managed like software. We also separated foundational (stable, domain‑anchored) from derived (composed/enriched for specific use‑cases) and showed how composition is the workhorse of value delivery. 

This third part makes that guidance concrete on Microsoft Fabric: why Fabric is a natural home for data products, which product types you can expose, and how to govern and compose them—including zero‑copy patterns and two near‑term preview capabilities: Materialized Lake Views and Shortcut Transformations.

Continue reading “Data Products in Fabric, Part 3: Why Fabric Is Ideal, What to Expose, and How to Govern (with zero‑copy patterns)”

Improvement Science for Business Leaders: A Practical Playbook for Better, Faster Results

Most executives know Lean, Six Sigma, and Agile. Improvement science is the disciplined backbone behind those methods—a way to get measurable gains by learning quickly in the real world, not just in the boardroom. It’s been refined for decades in healthcare and education, but its core ideas translate cleanly to sales, operations, CX, finance, HR, and product. Here’s what it is—and how to start using it immediately.

Continue reading “Improvement Science for Business Leaders: A Practical Playbook for Better, Faster Results”

“Zero Copy” Doesn’t Mean “No Copies.” It Means “No Unmanaged Copies.”

The rallying cry of modern data platforms—Zero Copy—is revolutionary because it flips the default: don’t move data unless there’s a good reason and the platform manages it for you. In Microsoft Fabric, that starts with in-place access via OneLake Shortcuts and an open storage layer, then selectively uses managed and automated copies (like Mirroring and Materialized Lake Views) when they deliver clear value. The result is less sprawl, more trust, and faster analytics—without hand-built duplication. 

Continue reading ““Zero Copy” Doesn’t Mean “No Copies.” It Means “No Unmanaged Copies.””

A Lightweight Ingestion Framework in Microsoft Fabric

Modern Fabric estates don’t need a forest of bespoke pipelines, but they do need metadata-driven tools to reduce time to insight. You can land data quickly in Bronze, promote it reliably to Silver and Gold with a metadata‑driven Spark Structured Streaming engine, and treat Gold as the foundation for your data products—semantic models, AI endpoints, and any other served formats.

Continue reading “A Lightweight Ingestion Framework in Microsoft Fabric”