Good database design balances redundancy, query speed and structural flexibility. This lab renders a small orders database as 3D geometry so you can see those trade-offs directly: tables as platforms of rows, foreign keys as arcing beams, and a query as a glowing pulse that has to physically travel through the data.
customers, orders and order_items tables linked by foreign keys, storing each fact once. Denormalizing merges everything into one flat table, which is simpler to query but repeats customer details on every order row (visualized as duplicate-colored blocks).log₂(rows) steps. With it off, the engine performs a full table scan, checking rows one by one until it finds a match.A full table scan on a million-row table checks roughly 500,000 rows on average to find one match; a B-tree index on the same table typically needs about 20 comparisons — the difference between milliseconds and seconds at scale.
A live, editable 3D database: reshape a small orders schema between normalized and denormalized form, switch between relational and NoSQL document models, toggle a B-tree index, and run a query to watch it physically travel through rows, index nodes and foreign-key joins.
Normalization removes duplicate data by splitting it across linked tables; denormalizing trades that redundancy for simpler reads. Indexing turns a linear scan into a logarithmic lookup, and NoSQL documents avoid joins by embedding related data directly.
Pick a table structure and data model, set how much data each table holds, toggle the index on or off, then press Run query and watch the glowing pulse trace its path — and read off how many rows it had to examine.
A composite B-tree index can turn a query that reads millions of rows into one that reads a few dozen — but every index also has to be updated on every write, which is the classic read/write trade-off in database design.