Chapter 09 · Index Engineering: Rowstore, Filtered, Included, Computed, and Specialized Indexes

Clustered vs Nonclustered Design, Key Width, Selectivity, and Clustering-Key Propagation

Design clustered and nonclustered rowstore indexes from access patterns, key width, stability, uniqueness, insert locality, and propagation cost—not selectivity alone.

Advanced120–155 minutesClustering-key propagation labSQL Server 2025 CU7 · 17.0.4065.4Compatibility 170 · Developer/ExpressLast reviewed: August 2026

Learning outcomes

ServiceHub has enough data and enough nonclustered indexes that a clustering-key choice now affects far more than one access path. In SQL Server rowstore, the clustered index leaf is the table, and its key is also carried as the row locator in every nonunique nonclustered index. A clustering key therefore changes table order, insert locality, page-split behavior, nonclustered index width, cache footprint, and lookup cost. This lesson turns that mechanism into a design process instead of the slogan “cluster on the most selective column.”

01

Choose clustering keys from access pattern, width, stability, uniqueness, and insert locality.

02

Explain why nonunique clustering keys receive an internal uniquifier where required.

03

Observe clustering-key propagation into nonclustered indexes.

04

Separate random-split pressure from sequential-key last-page contention.

05

Reject selectivity-only and one-size-fits-all clustered-index rules.

Chapter continuity

Chapter 08 exposed B+ tree structure, page splits, density, and row locators. Chapter 09 turns those mechanics into index portfolio decisions. Mandatory labs use free SQL Server 2025 Developer/Express and disposable lab09 objects inside ServiceHubLab.

1. A clustered index is a storage decision, not a badge of importance

A clustered index orders the rowstore table by its clustering key. There can be only one because the leaf level contains the data rows themselves. A nonclustered index is a separate B+ tree whose leaf rows contain its own key columns, optional included columns, and a row locator back to the base object. If the base table is clustered, that locator is the clustering key; if it is a heap, the locator is a RID.

Good clustering candidates are usually narrow, stable, frequently useful for access/order, and practical for the table's insert pattern. Uniqueness is useful because a nonunique clustered key may require SQL Server to add an internal 4-byte uniquifier to duplicate key rows. High selectivity is relevant, but it is not enough: a 200-byte highly selective natural key can make every nonclustered index substantially wider.

sql · create narrow and wide clustering-key probes
USE ServiceHubLab;GOIF SCHEMA_ID(N'lab09') IS NULL EXEC(N'CREATE SCHEMA lab09 AUTHORIZATION dbo;');DROP TABLE IF EXISTS lab09.NarrowCluster;DROP TABLE IF EXISTS lab09.WideCluster;CREATE TABLE lab09.NarrowCluster(  work_order_id bigint IDENTITY(1,1) NOT NULL,  external_ref varchar(80) NOT NULL,  opened_at datetime2(3) NOT NULL,  status varchar(20) NOT NULL,  customer_id int NOT NULL,  CONSTRAINT PK_NarrowCluster PRIMARY KEY CLUSTERED(work_order_id),  CONSTRAINT UQ_NarrowCluster_external UNIQUE(external_ref));CREATE INDEX IX_Narrow_Status ON lab09.NarrowCluster(status);CREATE TABLE lab09.WideCluster(  external_ref varchar(80) NOT NULL,  region_code char(6) NOT NULL,  opened_at datetime2(3) NOT NULL,  status varchar(20) NOT NULL,  customer_id int NOT NULL,  CONSTRAINT PK_WideCluster PRIMARY KEY CLUSTERED(external_ref,region_code));CREATE INDEX IX_Wide_Status ON lab09.WideCluster(status);GO

2. Prove propagation from metadata

The clustered key is automatically present in nonclustered indexes even when you did not type it in the CREATE INDEX statement. sys.index_columns lets you inspect key and included columns, but remember that some automatically carried clustering-key columns can be represented differently in metadata than explicitly declared key columns. The reliable design conclusion is architectural: every nonclustered leaf row needs a base-row locator, and a clustered table's locator is the clustering key.

sql · inspect index columns and physical size
SELECT OBJECT_NAME(i.object_id) AS table_name,       i.name AS index_name,i.type_desc,       c.name AS column_name,ic.key_ordinal,ic.is_included_columnFROM sys.indexes AS iJOIN sys.index_columns AS ic  ON ic.object_id=i.object_id AND ic.index_id=i.index_idJOIN sys.columns AS c  ON c.object_id=ic.object_id AND c.column_id=ic.column_idWHERE i.object_id IN (OBJECT_ID(N'lab09.NarrowCluster'),OBJECT_ID(N'lab09.WideCluster'))ORDER BY table_name,i.index_id,ic.key_ordinal,ic.index_column_id;GOSELECT OBJECT_NAME(p.object_id) AS table_name,i.name,       SUM(ps.reserved_page_count)*8.0/1024 AS reserved_mb,       SUM(ps.used_page_count)*8.0/1024 AS used_mbFROM sys.dm_db_partition_stats AS psJOIN sys.partitions AS p  ON p.partition_id=ps.partition_idJOIN sys.indexes AS i  ON i.object_id=p.object_id AND i.index_id=p.index_idWHERE p.object_id IN (OBJECT_ID(N'lab09.NarrowCluster'),OBJECT_ID(N'lab09.WideCluster'))GROUP BY p.object_id,i.nameORDER BY table_name,i.name;GO

On an empty or tiny table, size differences are too small to be meaningful. That is expected. Populate a representative row count before drawing conclusions, and treat resulting megabytes as a local observation rather than a universal ratio.

3. Insert locality changes the failure mode

Random keys distribute inserts across existing key ranges and can drive middle-page splits. Monotonically increasing keys usually append at the right edge, reducing random fragmentation but concentrating concurrent insert activity on the last page. That can create PAGELATCH_EX contention under high concurrency. Chapter 08 introduced OPTIMIZE_FOR_SEQUENTIAL_KEY; here the design lesson is that an ever-increasing clustering key trades one risk for another. You still need workload evidence.

sql · capture physical and operational evidence
DECLARE @obj int=OBJECT_ID(N'lab09.NarrowCluster');SELECT i.name,ps.page_count,ps.avg_page_space_used_in_percent,       ps.avg_fragmentation_in_percentFROM sys.indexes AS iCROSS APPLY sys.dm_db_index_physical_stats (DB_ID(),@obj,i.index_id,NULL,'LIMITED') AS psWHERE i.object_id=@obj;SELECT i.name,os.leaf_insert_count,os.leaf_allocation_count,       os.page_latch_wait_count,os.page_latch_wait_in_msFROM sys.indexes AS iCROSS APPLY sys.dm_db_index_operational_stats (DB_ID(),i.object_id,i.index_id,NULL) AS osWHERE i.object_id=@obj;GO

Operational counters are cumulative and reset-sensitive. A large number is not a diagnosis unless you know the observation window and compare it with throughput, latency, waits, and a relevant baseline.

4. Wrong approach: “cluster on the most selective column”

Selectivity describes how well a predicate narrows rows. It matters for access paths, but clustering-key design also affects every nonclustered index, insert locality, update cost, and row stability. A GUID can be extremely selective yet create random insertion points; a long natural key can be selective yet multiply storage. Conversely, a moderately selective date key might support range scans but be a poor unique row locator.

Repair the mental model

Score clustering candidates against the actual workload: common seeks/ranges/order, key width, update frequency, uniqueness, insert pattern, foreign-key use, nonclustered portfolio size, and concurrency. When no candidate dominates, a narrow surrogate clustering key plus unique natural-key constraint is often easier to govern—but it is still a design choice, not a law.

5. Production judgment and cleanup

Changing a clustered index on a large production table is a major operation. It can rebuild the base table and nonclustered indexes, consume log and temp/storage, block or use online/resumable features depending on edition/version/options, and alter replication/HA operational characteristics. Record the engine build, edition, available maintenance window, and rollback method before treating a clustering-key redesign as “just an index change.”

sql · cleanup lesson 1 probes
DROP TABLE IF EXISTS lab09.WideCluster;DROP TABLE IF EXISTS lab09.NarrowCluster;GO

Check your understanding

  1. Why does a clustering key affect nonclustered index width?
  2. Does highest selectivity automatically make the best clustering key?
  3. What risk can an ever-increasing key introduce under heavy concurrent inserts?
  4. What happens when a clustered key is not unique?
  5. What should you record before changing a production clustering key?
Review the answers

Because it is the base-row locator stored with nonclustered index entries on a clustered table.

No; width, stability, access patterns, insert locality and propagation cost also matter.

Last-page latch contention even though random page splits may be reduced.

SQL Server can add an internal uniquifier to duplicate key rows.

Build/edition, workload evidence, index dependencies, log/storage impact, maintenance method and rollback plan.

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.