Project the columns the whole plan needs
Calculate required column unions, distinguish output selectivity from physical reads, and reason about page-level late materialization.
On this page
The question for today
What you’ll understand
- Build a required-column set including filters, joins, and output expressions.
- Estimate logical projected bytes without promising proportional physical I/O.
- Explain how late materialization depends on page navigation and row alignment.
A query that returns two columns may need several more to decide which rows to return. Column projection removes unused fields from a scan, but its correct scope is the whole relevant query plan, not only the final output list. Once required fields are known, an engine can consider when to decode them. These decisions connect logical queries to page ranges, temporary buffers, and CPU work, so each layer needs its own measurement.
Required fields are a union, not a count of outputs
Consider SELECT price FROM events WHERE region='west'. Even though the output has only price, the scan generally needs region to evaluate the predicate unless trustworthy partition or other metadata already establishes it. Joins, grouping keys, sort expressions, and user-defined computations can require further fields. Track the union of required leaf paths so a field used twice is not counted twice. For a nested object, retrieving one child may still need validity or structural information that describes its ancestors; the storage representation determines those dependencies.
After projection, the reader locates column chunks and pages for selected row groups. It can often avoid fetching unrelated field payload entirely, which is a central advantage of columnar layout. But projection does not eliminate file metadata, structural levels, page headers, dictionaries for selected fields, or extra bytes brought in by request coalescing. Large fixed-size remote reads can also deliver neighboring bytes. Record which bytes were requested and which were useful rather than assuming that two selected fields in a ten-field schema always produce exactly one-fifth of the transferred data.
Late materialization avoids some work at physical granularity
An engine can evaluate selective filter columns first, produce matching row positions, and then fetch or decode other selected columns only where needed. This is late materialization. Its benefit depends on format navigation, engine support, page size, match distribution, and the cost of extra passes. A single matching row may still require decompressing the page containing it. If matches are scattered across every page, a one-percent result can still touch nearly all output-column pages, even while avoiding some value conversion or copying within them.
Page indexes and row-position information help align skips across columns with different page boundaries. They do not make every row independently compressed or randomly accessible at zero cost. Dictionary state may need to be loaded before a referenced data page can be interpreted, and nested fields add structural alignment requirements. Compare an eager scan and a staged scan on both clustered and scattered matches. Include all remote requests, decoder CPU, and intermediate position buffers so the chosen strategy improves the user's operation rather than only one internal counter.
Less data movement can change the next bottleneck
A narrow projection can shift a scan from storage transfer toward decoding, memory bandwidth, or aggregation CPU. Once one layer improves, remeasure before applying the same optimization again. For an AI feature pipeline, unused string columns can be expensive both on storage and in materialized objects, while a compact numeric aggregation can become limited by arithmetic or memory movement. File compression ratio alone cannot diagnose this transition. Use end-to-end latency and per-stage counters with a fixed result check to locate the remaining cost.
The roofline lab concerns a hypothetical floating-point kernel after data has been decoded, not disk throughput or integer decoder speed. Reset, set both intensity I=4 FLOP/byte and memory bandwidth B=50 decimal GB/s, predict a 200 GFLOP/s ceiling, then Play or Step. Reset, re-enter both I=8 and B=50, predict 400 GFLOP/s, then inspect. The compute roof stays 1,000 GFLOP/s and playback only traces the roof. Projection may change a real kernel's bytes per useful operation, but the lab does not project a file or measure a scan.
Work it through
Count a filter column that is not returned
One million flat nonnullable rows have one hundred eight-byte columns. A query returns two columns and filters using a third distinct column. No metadata eliminates the filter and no pruning occurs. Ignore encoding and headers.
The whole table has 1,000,000 × 100 × 8 = 800,000,000 bytes of logical payload, or 800 decimal MB.
The required union contains three columns, not two. Its logical scan payload is 1,000,000 × 3 × 8 = 24,000,000 bytes, or 24 MB.
If only one percent of rows survives, the two output columns contain 10,000 × 2 × 8 = 160,000 bytes. That result size does not imply the filter and output pages required only 160,000 physical bytes to read.
Projection reduces logical input payload from 800 MB to 24 MB in this simplified scan. Result selectivity and physical page skipping remain separate questions.
The living notebook
Explore the model
Interactive lab · roofline
A projected numeric kernel can change bottlenecks
Reset, set both I=4 FLOP/byte and memory B=50 decimal GB/s, predict 200 GFLOP/s, then Play or Step. Reset and re-enter I=8 and B=50 to predict 400. The fixed synthetic compute roof is 1,000 GFLOP/s; playback traces it. This is a post-decode floating-point ceiling, not a storage or Parquet scan predictor.
- Attainable ceiling
- 640 GFLOP/s
- Ridge intensity
- 12.5 FLOP/byte
- Ceiling regime
- Memory-bound ceiling
Synthetic roofline ceiling min(1000, B·I), in GFLOP/s, with B in decimal GB/s and I in FLOP/byte. Playback traces the roof without changing the selected kernel. This upper-bound model is not a measured throughput prediction, hardware comparison or price model. Parameter changes pause playback and recompute the model at the current elapsed time. Reset restores both parameters and returns time to zero. Playback starts paused. Adjust either parameter, then choose Play or Step.
Look forReducing bytes per useful operation can expose another bottleneck, but format I/O and computation need separate models.
Pause & reason
Check your understanding
Your turn
Try it yourself
A query over 200,000 rows returns an eight-byte value and filters on a separate four-byte key. Estimate required logical input payload before pruning and explain why ten matching rows do not determine physical read bytes.
Take a moment to make a prediction or work it out on paper. Open a hint when you need one.
Give me a hint
Include both dependencies, then consider compressed page boundaries.
Compare with the solution
The required logical payload is 200,000 × (8 + 4) = 2,400,000 bytes before encoding, headers, and validity information. Ten result rows can be scattered across pages, and reading one value can require its containing page and dictionary. Filter pages and metadata are also needed, so result row count alone cannot determine physical bytes fetched.
Keep the idea
Take it with you
- Project the union of output and intermediate query dependencies.
- Late materialization works at format and reader granularity rather than making every row an independent read.
- Measure requested bytes, decoder work, and post-decode computation separately as bottlenecks move.
Read further
Original sources and deeper explanations to follow at your own pace.
Optional progress, saved in this browser when available. Revisit any lesson at any time.
Lesson not marked complete.