Chapter 17 · Physical Warehouse Design: Schemas, Tables, Constraints, Partitioning, Clustering, and Distribution

Logical Dimensional Model vs Physical Implementation in Row Stores, Column Stores, and Cloud Warehouses

Separate the stable dimensional contract from physical storage and access paths, then observe how the same AtlasMart questions behave in a row-store harness, partitioned files, and engine-specific access structures.

Intermediate → Advanced130–155 minutesLogical-vs-physical evidence labPython 3.13.5 · SQLite 3.46.1 · local/syntheticLast reviewed: September 2026

Learning outcomes

AtlasMart has a trusted dimensional model and a recoverable incremental pipeline. Analysts now complain that a daily revenue query, a seven-day product query, and a customer-window query scan more data than necessary. The wrong response is to redesign the grain because a query is slow. The correct question is: which physical representation lets the same logical facts answer the same questions with less work?

01

Separate logical grain, facts, dimensions, metrics, and history semantics from physical row/column/cloud implementations.

02

Explain what a physical access path can change—scan work, locality, maintenance, load behavior—without changing metric meaning.

03

Read SQLite EXPLAIN QUERY PLAN evidence for full scans versus index-assisted searches without generalizing the syntax to other engines.

04

Keep benchmark-scale synthetic data distinct from canonical AtlasMart production controls and business truth.

05

Recognize which observations require re-measurement when moving from a local row store to a column store or cloud warehouse.

Chapter 17 continuity contract

Physical design must not redefine the business model. The accepted Chapter 16 current controls remain 9 paid order-line facts, 7 paid orders, 11 units, 740 USD paid GMV, 450 USD cost-at-sale, and 290 USD gross profit. The benchmark harness expands those nine rows deterministically to 21,600 synthetic rows only to make physical-access differences observable. Benchmark-scale totals are test data, not new production controls. Grain, conformed dimension meaning, SCD history policy, metric formulas, source contracts, and Chapter 15–16 recovery semantics remain unchanged.

Execution and guarantee boundary

The mandatory lab is local, synthetic, and free. Generation-time validation ran with Python 3.13.5 and SQLite 3.46.1 in UTC. SQLite is used only as a row-store/access-path harness; it has no native cloud-warehouse clustering or distributed distribution keys. Date/month/hash file layouts are deterministic JSONL simulations used to calculate files and bytes that a dispatcher would scan. The lab never claims those numbers are Snowflake, BigQuery, Redshift, Databricks, ClickHouse, or DuckDB behavior. EXPLAIN QUERY PLAN evidence is SQLite-specific and its output format is documented as unstable across SQLite versions; the lessons interpret only the observed SCAN/SEARCH mechanism, not the literal formatting.

1. The logical model is the contract; the physical model is an implementation

The logical dimensional model states what one fact row means, which dimensions provide context, which keys preserve identity/history, and which measures are valid at that grain. A physical implementation chooses database/schema boundaries, table/index structures, file layout, partitions, clustering/sort order, distribution, compression, and engine-specific storage options.

Changing from a heap-like row table to an indexed table, partitioned files, a column store, or a disaggregated cloud warehouse should not change “one row = one paid order line,” nor should it turn 740 USD into a different revenue definition. If physical optimization changes query results, the optimization has crossed the semantic boundary and must be treated as a model change.

2. Row stores, column stores, and cloud warehouses optimize different mechanics

Implementation family Typical physical strength What remains logical What must be re-measured
Row store Selective key/index access, point/range retrieval, transactional maintenance Grain, metric formula, dimension semantics Index benefit, cache behavior, write amplification, join plan
Column store Projection-heavy scans, compression, vectorized analytical execution Same facts/dimensions and query semantics Scan bytes, encoding/compression, segment pruning, update/load cost
Cloud/disaggregated warehouse Elastic compute, remote object storage, engine-managed micro-partitions/metadata Same analytical contract Credits/slots/scan billing, cache, clustering maintenance, concurrency, cold/warm state

These are implementation families, not mutually exclusive product labels. A system can combine row-oriented metadata, columnar data, remote object storage, caches, indexes, and automatic clustering. Therefore the design artifact must state the engine and evidence, not merely “we use a cloud warehouse.”

3. One query corpus, two SQLite access paths

The local harness loads the same 21,600 benchmark rows into two SQLite databases. One has only the fact-table primary key; the other adds composite indexes on (order_date, product_id) and (customer_id, order_date). The logical row checksum is identical.

Query Predicate Result Heap plan Indexed plan
Q1 daily GMV date = one day 29,600 Heap: SCAN fact_sales Indexed: SEARCH by order_date
Q2 product in 7-day range date range + product 58,800 Heap: SCAN Indexed: SEARCH date/product index
Q3 customer in 21-day range customer + date range 214,200 Heap: SCAN Indexed: SEARCH customer/date index
Q4 full GMV no selective predicate 1,776,000 SCAN Index does not remove full-scan work
inspect_plans.py
import jsonfrom pathlib import Pathr=json.loads(Path("atlasmart_ch17_lab/reports/benchmark_report.json").read_text())for q in ["Q1_daily_gmv","Q3_customer_window"]:    print(q)    print(" heap   ", r["plans"][q]["heap_plan"])    print(" indexed", r["plans"][q]["indexed_plan"])# Generation environment:# Q1 heap:    SCAN fact_sales# Q1 indexed: SEARCH fact_sales USING INDEX idx_fact_sales_date_product (order_date=?)# Q3 indexed: SEARCH fact_sales USING INDEX idx_fact_sales_customer_date (...)
What the evidence proves

For SQLite 3.46.1 and this exact schema/query corpus, the indexed database uses selective SEARCH access for Q1 and Q3 while the heap database scans the fact table. It does not prove that another engine will use the same index, syntax, plan text, or latency ratio.

4. Measured timings are secondary evidence, not universal promises

The generator executed each query 15 measured times after three warm-ups without flushing the SQLite or operating-system cache. Q1 measured 0.6883 ms median on the heap database versus 0.0585 ms on the indexed database in this environment; Q4, the full-table aggregate, measured 1.0088 ms versus 1.0346 ms. The important lesson is not those tiny numbers. It is that an access structure helped selective queries but did not remove work from a logically full scan.

Never turn a local microbenchmark into a production SLA. Report dataset size, cache state, concurrency, engine, storage, query text, warm-up policy, and measured distribution—or use deterministic scan calculations when those are the real decision variable.

5. Controlled failure: change grain to “make the table faster”

A team may propose daily pre-aggregation as the new base fact because the dashboard asks for daily GMV. That can accelerate one query but destroys order-line evidence needed for product/customer drilldown, returns, restatements, and future metrics. The repair is to preserve the atomic fact and add a physical access path or explicitly governed aggregate later. Physical design should optimize access to the logical model, not silently replace it.

6. Hands-on: run the physical harness

Save the Chapter 17 lab script from Lesson 5 as ch17_lab.py and run it with the same Python interpreter used for the course. It creates disposable databases, file layouts, a benchmark report, and a physical-design decision record.

run_ch17_lab.sh
python ch17_lab.py# Expected identity lines:# runtime: Python 3.13.5 / SQLite 3.46.1 / UTC# continuity controls: (9, 7, 11, 740, 450, 290)# benchmark rows: 21600# decision logical_model_changed: False# cleanup: rm -rf atlasmart_ch17_lab

Use the printed runtime rather than assuming your SQLite build matches the generation environment. If your version differs, re-read the plan and re-measure.

7. Production judgment and bridge

The physical-design question is “what work can be avoided or localized for the representative workload while preserving semantic correctness?” The answer may involve partitions, indexes, clustering, sort order, distribution, aggregates, caching, or nothing at all. Chapter 17 evaluates those choices from evidence and rollback cost, not from fashion.

Lesson 2 makes the deployment boundary explicit: schemas/databases, naming, ownership, constraints, and environment/domain separation determine who can safely operate and evolve these physical objects.

Knowledge check

Check your understanding

  1. What must remain unchanged when physical layout changes?
  2. What does SQLite SEARCH versus SCAN prove here?
  3. Why is Q4 not helped by a selective index in the lab?
  4. Why is the 21,600-row benchmark not a new AtlasMart production state?
  5. When must physical advice be re-measured?
Review the answers

1. The declared grain, fact/dimension semantics, history policy, and metric definitions unless a separately governed model migration is approved.

2. Only the access strategy SQLite 3.46.1 chose for this exact schema/query; it is evidence for the local harness, not a cross-engine guarantee.

3. It intentionally aggregates the entire fact table, so there is no selective predicate that removes logical rows.

4. It is a deterministic scale expansion used only for physical evidence; canonical continuity controls remain 9/7/11/740/450/290.

5. When engine, version, storage, data distribution, query corpus, cache/concurrency, or layout changes materially.

Authoritative references

Keep knowledge open

Help the academy stay free and grow.

If these tutorials save you time, a small donation supports new lessons, technical review, diagrams, examples, and long-term maintenance.

ETHEthereum / ERC-20 only
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0

Send only Ethereum or ERC-20 compatible assets to this address.