How does each storage model identify a row over time — a heap with physical row identifiers versus a clustered table keyed on the primary key — and what happens when a row moves or its primary key value is updated?
answer
- Heap = physical address; clustered = logical key reference
- Splits move rows → clustered must use logical refs
- Heap row growth → forwarding pointer, extra hop
- Update clustering key = delete + reinsert + rewrite all secondaries
- Argues for narrow immutable surrogate PK
basics
~20 sA heap identifies rows by physical address, stable until the row is relocated, at which point engines leave a forwarding pointer or update index entries. A clustered table identifies rows logically by primary key, so rows may move between pages freely, but changing the key value physically relocates the row.
solid answer
~60 s**Heap:** a row's identity is its physical **row id** (file, page, slot). Index leaves store that id. It is stable across ordinary inserts, which is the model's simplifying advantage. But an update that makes the row too large for its page forces relocation, and the engine either leaves a **forwarding pointer** at the old slot (lookups then take an extra hop until reorganization) or relocates and repairs the referencing index entries. Repeated relocation is a recognized heap fragmentation symptom. **Clustered:** identity is the **clustering key value**, a logical reference. Rows move constantly — every page split relocates them — and that is fine, because no stored reference names a physical address. The cost is paid at lookup time: secondary indexes hold the key, so reaching the row requires a second descent. **Updating the primary key of a clustered row** is not an in-place edit: the row belongs somewhere else in key order, so it is effectively deleted and re-inserted at the new position — a page write in two places, possible splits, and index maintenance. This is a good reason for immutable, meaningless primary keys.
go deeper
Know that heaps address rows by physical location while clustered tables reference rows by their key value.
Explain why splits force clustered storage to use logical references, and that changing the key relocates the row.
Add the maintenance picture: heap forwarding pointers and reorganization, and the full write amplification of a clustering-key update across all secondary indexes.
Use it to argue key policy — immutable narrow surrogate keys, natural attributes as constrained columns, and never exposing physical identifiers externally.
## Two notions of row identity Every index needs a way to say "the row I mean is *that* one". There are only two families of answer, and the storage model picks one. **Physical identity (heap).** The reference is an address: file number, page number, slot number. Resolving it is trivial — read that page, take that slot. Every index leaf entry stores such an address. **Logical identity (clustered).** The reference is the clustering key value. Resolving it means searching the clustered B+tree for that key. Slower to resolve, but immune to physical movement. The choice is forced by the model. A clustered table constantly relocates rows as leaf pages split, so physical addresses stored in secondary indexes would be invalidated en masse by a single split — every split would require updating every secondary index that referenced the moved rows. Logical references sidestep this entirely. Conversely, a heap does not relocate rows on insert, so a physical address is safe and much cheaper to follow. ## What happens in a heap when a row must move Rows in a heap are stable in position under normal operation, but not unconditionally. The common trigger is an update that grows the row — a variable-length column gets longer — so it no longer fits in the free space of its page. Engines take one of two approaches: - **Forwarding pointer.** Leave a small stub at the original slot pointing at the row's new location. All existing index entries keep working; they resolve to the stub, which redirects. The cost is an extra page access per lookup for that row. Accumulated forwarding is a measurable fragmentation problem, and the standard remedy is a table reorganization or rebuild that relocates rows and repairs references. - **Relocate and repair.** Move the row and update every index entry that pointed at the old address. No lookup penalty afterwards, but the update itself becomes much more expensive, touching every index on the table. Engines also differ in whether an update writes a new row version elsewhere as part of concurrency control; where they do, the same address-stability question arises and is handled with similar machinery. ## What happens in a clustered table Row movement is routine and cheap in terms of references: when a leaf page splits, roughly half its rows move to a new page, and *nothing* needs fixing in the secondary indexes because they never named a physical location. This is precisely the property that makes clustering viable. The price is paid on every secondary read: two descents rather than one descent plus a direct fetch. It is a permanent, per-lookup tax in exchange for zero maintenance on movement. ## Updating the clustering key itself This is the case interviewers probe. In a clustered table the key determines *where the row lives*, so changing it changes the row's address. The engine cannot edit in place; it must: 1. Remove the row from its current leaf page (leaving free space, possibly triggering merge/underfill handling). 2. Insert it at the position implied by the new key, which may split the destination page. 3. Update every secondary index entry, because those entries stored the old key value as the row reference — so all of them must now point at the new one. So a single-column update becomes a delete-plus-insert on the table plus a write to every secondary index. Compare a heap: updating the primary key column there rewrites one column in place and updates only the primary key index; the row does not move and other indexes are untouched. This asymmetry is a strong practical argument for **immutable, meaningless primary keys**. If the primary key is a natural attribute that the business might change — an email address, a business reference code, a document number — it is a poor clustering key, both because it is often wide and because updates to it are expensive and invalidate references everywhere. A narrow surrogate key that never changes gives stable identity, cheap clustering, and lets the mutable natural value live in an ordinary column with a uniqueness constraint. ## Related consequences worth naming - **Application-visible identifiers.** A physical row id is not a durable business identifier; it can change under reorganization, so it should never leak into application data or external references. A clustering key value is a logical identity and is safe to expose — which is another reason it should be immutable. - **Reorganization semantics.** Rebuilding a heap changes row addresses and therefore rewrites every index; rebuilding a clustered table rewrites the table in key order but leaves secondary index *contents* logically unchanged. ## The answer in short "Heaps use physical row ids — cheap to follow, stable on insert, but an update that grows a row forces relocation with a forwarding pointer or index repair. Clustered tables use the clustering key as a logical reference, so rows can move freely on splits with no index maintenance, at the cost of a second descent on every secondary lookup. And updating the clustering key is a delete-plus-insert that also rewrites every secondary index entry — which is why the clustering key should be narrow and immutable."
- Why is updating the primary key of a clustered row so much more expensive than updating any other column?Because the key determines the row's physical position, so changing it relocates the row: the engine deletes it from its current leaf page and re-inserts it where the new key belongs, possibly splitting the destination page. On top of that, every secondary index stores the primary key as its row reference, so all of those entries must be rewritten to the new value. A non-key column update touches only the row and any index that includes that column.
- What is a forwarding pointer in a heap, and why does it matter operationally?When an updated row grows beyond the free space on its page, the engine can move it and leave a stub at the old slot pointing to the new location, so existing index entries remain valid. Lookups for that row then take an extra page access following the stub. As forwarded rows accumulate, read latency and I/O rise, and the fix is a table reorganization or rebuild that places rows properly and clears the indirection.
- Should physical row identifiers ever be exposed to the application as durable keys?No. They are physical addresses that can change under row relocation, table reorganization or rebuild, so any external reference to them can silently become wrong or point at a different row. Durable identity should come from a declared key column that the database guarantees, ideally an immutable surrogate. Row ids are an internal implementation detail for index-to-row resolution.
A heap reference is a street address — precise, but useless the day you move; a clustered reference is a person's name — always resolvable, but you have to look it up in the directory each time.
saying these in an interview costs you the question
- Saying secondary indexes in a clustered table store physical addresses
- Assuming updating a primary key is an ordinary in-place column update
- Believing heap row ids never change under any circumstance
- Treating physical row identifiers as safe durable application keys
- Ignoring that a clustering-key update also rewrites every secondary index entry