An application records orders, a CRM records contacts, a marketing tool records campaigns. To answer “which channel brings in the most loyal customers?”, you need to bring this data together in one place, historize it and make it easy to query, without slowing down the production databases. That is the role of the analytical data platform. This course teaches you how to model it; this first lesson sets out the vocabulary.
The data warehouse
A data warehouse is a database designed for analysis. The data in it is structured (typed tables and columns), integrated (a customer has the same identifier whatever the source), historized (past states are kept) and organized for aggregation queries over large volumes. Product examples: BigQuery, Snowflake, Amazon Redshift, Azure Synapse, or PostgreSQL for modest volumes. Most store data by column: a query that reads three columns of a fifty-column table only reads those three columns.
The schema is applied at write time (schema on write): data that doesn’t respect the expected type is rejected at load.
The data lake
A data lake is low-cost file storage, often object storage (Amazon S3, Azure Data Lake Storage, Google Cloud Storage). You drop everything in as is: CSV exports, JSON events, Parquet files, images, logs. The schema is applied at read time (schema on read): the query engine (Spark, Trino, etc.) interprets the files.
Advantage: you keep everything, at low cost, including what you don’t yet know the use of. Risk: without a catalog or rules, the lake becomes a “swamp” where nobody knows which files are reliable.
The lakehouse
The lakehouse combines the two: the data stays in open files (often Parquet) on object storage, but a table format (Delta Lake, Apache Iceberg, Apache Hudi) adds on top what the lake was missing: transactions, a controlled schema, row updates and deletions, version history. Several engines can read the same tables.
| Warehouse | Lake | Lakehouse | |
|---|---|---|---|
| Data | structured | any format | structured and semi-structured |
| Schema | on write | on read | controlled by the table format |
| Querying | SQL | various engines | SQL and various engines |
The choice depends on volumes, uses (reporting, data science) and the team’s skills; the modeling you are about to learn is the same in all three cases.
ETL or ELT
Historically, you would extract, transform on a dedicated server, then load the result (ETL). With powerful, usage-billed warehouses, you tend to do ELT instead: you load the raw data first, then transform it inside the warehouse, in SQL. Tools like dbt (course DE2) organize these transformations.
The layered architecture
Transformations are arranged in successive layers. Names vary by team:
- Raw (bronze, raw): a faithful, timestamped copy of the sources, never modified. You can always recompute everything from it.
- Cleaned (silver, staging): correct types, harmonized column names, normalized values, load duplicates removed. One table per source, with no business logic.
- Business (gold, marts): analysis-ready models, typically star schemas, with the business rules (“a cancelled order doesn’t count in revenue”).
Here is a typical transformation from the raw layer to the cleaned layer. The source is loaded several times a day: the same order may appear in two loads; we keep the most recent version.
WITH classees AS (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY id ORDER BY _loaded_at DESC) AS rang
FROM raw_commandes
)
SELECT CAST(id AS INTEGER) AS commande_id,
lower(trim(client)) AS client,
CAST(montant AS REAL) AS montant,
upper(devise) AS devise
FROM classees
WHERE rang = 1;
Notice that the cleaned layer does not filter incomplete rows (a missing amount stays NULL): it makes them visible. Deciding that an order with no amount is excluded from a metric is a business rule, which belongs to the next layer.
Exercises
Solutions and tests stay on the server. In the student space, your code is run and checked, and the tutor gives hints before the solution.
From the raw layer to the cleaned layer
Raw table:
raw_commandes(id, client, montant, devise, _loaded_at), all columns as text;_loaded_atis the load timestamp.Write the cleaned layer query. For each
id, keep only the most recent load, and produce:commande_id: the id as an integer;client: the name without surrounding spaces, in lowercase;montant: the amount as a real number (leave NULL if absent);devise: the currency in uppercase.
Exercise data (SQL)
-- Couche brute (bronze) : chargée deux fois par jour, sans transformation CREATE TABLE raw_commandes (id TEXT, client TEXT, montant TEXT, devise TEXT, _loaded_at TEXT); INSERT INTO raw_commandes VALUES ('1', ' amina ', '49.90', 'EUR', '2026-03-01 02:00'), ('2', 'BASTIEN', '120', 'eur', '2026-03-01 02:00'), ('2', 'BASTIEN', '125', 'eur', '2026-03-02 02:00'), ('3', 'Camille', '15.5', 'EUR', '2026-03-01 02:00'), ('4', 'camille', NULL, 'EUR', '2026-03-02 02:00');WITH classees AS ( SELECT *, ROW_NUMBER() OVER (PARTITION BY ... ORDER BY ...) AS rang FROM raw_commandes ) SELECT ... FROM classees WHERE rang = 1;Volume check between layers
Raw table:
raw_commandes(id, client, montant, devise, _loaded_at), all columns as text;_loaded_atis the load timestamp.Before publishing the cleaned layer, you check the volumes. Display on one row the number of raw rows (alias
lignes_brutes) and the number of distinct orders (aliascommandes).Exercise data (SQL)
-- Couche brute (bronze) : chargée deux fois par jour, sans transformation CREATE TABLE raw_commandes (id TEXT, client TEXT, montant TEXT, devise TEXT, _loaded_at TEXT); INSERT INTO raw_commandes VALUES ('1', ' amina ', '49.90', 'EUR', '2026-03-01 02:00'), ('2', 'BASTIEN', '120', 'eur', '2026-03-01 02:00'), ('2', 'BASTIEN', '125', 'eur', '2026-03-02 02:00'), ('3', 'Camille', '15.5', 'EUR', '2026-03-01 02:00'), ('4', 'camille', NULL, 'EUR', '2026-03-02 02:00');SELECT COUNT(*) AS lignes_brutes, ... AS commandes FROM raw_commandes;Choose an architecture
A team wants to keep JSON logs and product images at low cost, while offering analysts reliable SQL tables with row updates. Which architecture fits best?
- A classic data warehouse only
- A lakehouse: open files on object storage with a table format such as Delta Lake or Iceberg
- A simple shared folder of CSV files
End-of-lesson quiz
What does “schema on read” mean?
- The schema is checked at load time
- The schema is applied by the engine when the files are read
- Data never has a schema
In which layer do you put the rule “a cancelled order doesn’t count in revenue”?
- Raw
- Cleaned
- Business
Why is the raw layer never modified?
- Because SQL forbids it
- So that all transformations can be replayed from the original data
- To save space
What characterizes ELT?
- Data is transformed before being loaded
- Raw data is loaded and then transformed in the warehouse
- Data is never transformed
The rest of the course
- Building a Star Schema in SQL20 min
- Slowly Changing Dimensions (SCD)20 min
- Partitioning, Clustering and Aggregate Tables18 min
- Controlling a Warehouse's Costs16 min