Where we are in the pipeline
In b2 and b3 you landed Daybreak's data - as CSV, JSON, and Parquet, without dwelling on why. Now we make that choice on purpose. This is the storage stage of the lifecycle: once data has landed, the format it sits in decides how much it costs to keep and how fast every downstream query runs. Pick well here and the transform stage (b5) flies; pick badly and everything after pays.
Row formats vs columnar 6 min live
Formats split into two families. Row formats (CSV, JSON) store one record at a time, as human-readable text - easy to eyeball, but uncompressed, untyped, and fat. Columnar formats (Parquet) store one column at a time, as compressed typed binary - unreadable by eye, but small and blisteringly fast for the column-slicing that analytics does all day.
LiveWhy columnar is fast for analytics3 min▶
The whole difference is layout on disk. Analytics rarely wants whole rows - it wants "sum this one column across a billion records". Layout decides how much work that takes:
- Row format (CSV/JSON): values are interleaved record by record. To sum one column, the engine must read every byte of every row and skip the rest. All that I/O, mostly wasted.
- Columnar (Parquet): each column is stored together. To sum one column, the engine reads only that column's block and ignores the rest entirely. Less I/O, and similar values sit together so they compress hard.
- Types are baked in: Parquet records that a column is a DATE or an INTEGER. CSV is all text, so every read re-parses and re-guesses types - slow and error-prone.
Why the lake is full of Parquet. When a dashboard query touches 3 columns of a 200-column table, columnar storage reads roughly 3/200ths of the bytes; row storage reads all of it. On big tables that is the difference between a query that returns in a second and one that grinds for minutes - which is why analytical lakes standardised on Parquet.
Self-studyWhen readable formats still win2 min read▶
Parquet is not always right. CSV and JSON keep earning their place at the edges of a pipeline. CSV is the universal interchange format - every tool, every partner, every spreadsheet reads it, so it is often what data arrives as and what a non-technical stakeholder wants exported. JSON is the right shape for nested, semi-structured, still-changing data from APIs. The rule: readable formats for the human-facing and fluid edges, Parquet for the machine-facing analytical core.
Format round trips 6 min live
The fastest way to feel the difference is to write the same data three ways and read it back. Notice what survives the trip: Parquet remembers that order_date is a DATE, while CSV hands it back as plain text you have to re-parse. That lost type is the hidden tax of row formats.
LiveOne table, three formats3 min▶
Write orders to CSV, JSON, and Parquet, then read one column back from each and check its stored type. Press ▶ Run (first run caches the ~8 MB engine).
-- land the same data three ways
COPY (SELECT * FROM orders) TO 'f/orders.csv' (FORMAT CSV, HEADER);
COPY (SELECT * FROM orders) TO 'f/orders.json' (FORMAT JSON);
COPY (SELECT * FROM orders) TO 'f/orders.parquet' (FORMAT PARQUET);
-- read one value back from each and see what type it is
(SELECT 'parquet' AS fmt, typeof(order_date) AS date_type
FROM 'f/orders.parquet' LIMIT 1)
UNION ALL
(SELECT 'csv', typeof(order_date)
FROM read_csv_auto('f/orders.csv') LIMIT 1);
LiveReading a subset from Parquet is cheap3 min▶
Because Parquet stores columns separately, reading two of them touches only those two columns' data - the engine never loads the rest. Land the full table, then pull just two columns back.
COPY (SELECT * FROM orders) TO 'f/orders.parquet' (FORMAT PARQUET); -- only these two columns' data is read from the file SELECT order_id, order_date FROM 'f/orders.parquet' LIMIT 8;
Table formats: Iceberg, Delta, Hudi 5 min live
Parquet is a file. A pile of Parquet files is not a table - you cannot safely update it, evolve its schema, or ask "what did this look like last Tuesday". Table formats - Iceberg, Delta Lake, Hudi - add a metadata layer on top of Parquet files that brings database powers to the lake. This is what turns a data lake into a "lakehouse".
Self-studyWhat table formats add on top of Parquet3 min read▶
A table format keeps a manifest of which Parquet files make up the table right now, plus a log of changes. That metadata unlocks three things plain files cannot do:
- Transactions (ACID): a write either fully lands or not at all. No more readers catching a half-written table mid-update.
- Schema evolution: add, rename, or drop a column without rewriting every file. The metadata tracks the change.
- Time travel: query the table as it was at an earlier version or timestamp - for audits, debugging, and reproducibility.
The format bake-off ★ 6 min · everyone builds
Land the same orders to CSV and Parquet, read both back, and put them head to head. The row counts match exactly - same data - but the stored type of the date column does not: Parquet keeps DATE, CSV hands back text that needs re-parsing. That single difference is why the analytical core runs on Parquet.
Land twice: write orders to a CSV and a Parquet file from the same source query.
Count both: confirm the row counts are identical - no data lost either way.
Compare types: read the date column's type from each. Parquet = DATE, CSV = a re-parsed value.
-- land the same data as CSV and Parquet
COPY (SELECT * FROM orders) TO 'bo/orders.csv' (FORMAT CSV, HEADER);
COPY (SELECT * FROM orders) TO 'bo/orders.parquet' (FORMAT PARQUET);
-- counts match, but the stored date type does not
SELECT 'parquet' AS fmt,
(SELECT count(*) FROM 'bo/orders.parquet') AS rows,
(SELECT typeof(order_date) FROM 'bo/orders.parquet' LIMIT 1) AS date_type
UNION ALL
SELECT 'csv',
(SELECT count(*) FROM read_csv_auto('bo/orders.csv')),
(SELECT typeof(order_date) FROM read_csv_auto('bo/orders.csv') LIMIT 1);
Lost types cause silent bugs. A pipeline that lands to CSV and re-reads it often re-guesses a date as a string or a zip code as a number. Downstream a sort goes alphabetical instead of chronological, or leading zeros vanish. Parquet's baked-in types remove that whole class of bug - the data comes back exactly as it went in, which is why it is the default for anything a machine will read again.
Your turn: round-trip and verify ★ 8 min · build your own
Each editor starts fresh from Daybreak's raw source. Write to a format, read it back, and verify the trip was clean. All three run against the source tables.
LiveQ1 · Round-trip order_items through Parquet3 min▶
Write order_items to Parquet, read it back, and verify the row count matches the source - a clean round trip loses nothing.
COPY (SELECT * FROM order_items) TO 'rt/items.parquet' (FORMAT PARQUET);
SELECT (SELECT count(*) FROM 'rt/items.parquet') AS file_rows,
(SELECT count(*) FROM order_items) AS source_rows;
LiveQ2 · Land a filtered query, then aggregate4 min▶
COPY only completed orders to Parquet, read them back, and aggregate - count orders per channel straight from the landed file.
COPY (SELECT * FROM orders WHERE status = 'completed') TO 'rt/completed.parquet' (FORMAT PARQUET); SELECT channel, count(*) AS completed_orders FROM 'rt/completed.parquet' GROUP BY channel ORDER BY completed_orders DESC;
Self-studyQ3 · When is JSON the right choice?3 min▶
A thought exercise, no SQL. Parquet wins for the analytical core - so when would you deliberately keep data as JSON?
- Nested, semi-structured source data: an API returns orders with a variable list of line items and optional metadata. JSON stores that nesting natively; forcing it into flat columns too early loses shape.
- Schema still fluid: when the source keeps adding and dropping fields, JSON absorbs the change without a rewrite. You land raw JSON, then flatten to Parquet once the schema settles.
- The pattern: JSON at the raw landing edge for messy, changing sources; Parquet for the cleaned, stable, analytics-facing tables. Right tool, right stage.
Try it yourself - this week ◐ 20-30 min total
- Audit one pipeline at work: what format does each stage land in? Flag any analytical table still sitting in CSV or JSON.
- Take Demo 1 and add JSON as a third contender - compare all three row counts and date types in one result.
- Find one CSV-driven job that has hit a type bug (dates as text, dropped leading zeros). Note what switching it to Parquet would fix.
- Write down, for your team, the one-line rule for CSV vs JSON vs Parquet so the next person does not have to rediscover it.
- Skim the Iceberg or Delta docs for "time travel" and bring one use case to session b5 - transformation is next.
Official sources covered
This session teaches the working core of the DeepLearning.AI Data Engineering Professional Certificate (Joe Reis) and Reis & Housley's Fundamentals of Data Engineering, run on a live engine instead of slides. This page covers:
Three questions before you go 🎯 ◐ 90 seconds
1 · What is the core trade-off between Parquet and CSV?
CSV is readable text but uncompressed and typeless. Parquet is columnar binary - small, type-preserving, and fast for the column-slicing analytics does, at the cost of not being eyeball-readable.
2 · What do table formats (Iceberg, Delta, Hudi) add on top of Parquet files?
A table format adds a metadata layer over Parquet files, bringing ACID transactions, schema evolution, and time travel - turning a pile of files into a lakehouse table.
3 · When should you keep source data as JSON rather than convert to Parquet?
JSON natively holds nesting and absorbs schema change, so it fits messy, evolving API sources at the raw edge. Flatten to Parquet once the shape stabilises for the analytical core.