Chapter 08 · B-Tree, Bitmap, Function-Based, Domain, and Specialized Indexes

B-Tree Index Structure, Leaf Blocks, Branches, Rowids, Selectivity, and Clustering Factor

Build an Oracle B-tree mental model from branch and leaf blocks through rowids, access-path choices, selectivity, and clustering factor—then verify why cardinality alone never proves an index is beneficial.

Intermediate → Advanced110–130 minutesB-tree + clustering-factor labOracle AI Database 26ai · RU 23.26.3 baselineOracle AI Database Free · SQLcl/SQL*Plus/SQL DeveloperLast reviewed: August 2026

Learning outcomes

ServiceHub’s work-order table has grown enough that a query on a highly distinct ticket number is fast with an index, while a report on a similarly distinct timestamp still prefers a full scan. A developer concludes that one index is “fragmented” and proposes rebuilding it nightly. That diagnosis confuses three separate ideas: the B-tree’s structure, the fraction of rows a query needs, and how index-key order correlates with the table’s physical row placement. This lesson makes those mechanisms observable before any tuning decision.

01

Trace a B-tree lookup from root/branch blocks to leaf entries and heap-table rowids.

02

Distinguish unique, range, full, fast-full, and skip scans by access semantics rather than memorized plan names.

03

Use selectivity together with table-access cost instead of treating high cardinality as an automatic indexing rule.

04

Interpret BLEVEL, LEAF_BLOCKS, DISTINCT_KEYS, and CLUSTERING_FACTOR from USER_INDEXES/ALL_INDEXES.

05

Explain why clustering factor describes table-order correlation and is not a generic index-fragmentation score.

Prerequisite connection

Chapter 03 established blocks, segments and datafiles; Chapter 07 established buffer-cache consistent gets and row locking. An ordinary heap-table B-tree is a separate segment whose leaf entries point to table rows by physical rowid. Query cost therefore depends on both index navigation and the table blocks those rowids lead to.

Lab and version baseline

Mandatory examples target a disposable ServiceHub schema in Oracle AI Database Free 26ai and were reviewed against RU 23.26.3, SQL Developer 26.2, and SQLcl 26.2.1. Free limits itself to 2 foreground CPU cores, 2 GB combined SGA/PGA memory, and 12 GB user data, and Oracle does not provide patches or Support service requests for Free. No Diagnostics Pack or Tuning Pack is required in this chapter. Always verify the current Licensing Information manual for a production offering because a feature included in Free can require an extra-cost option elsewhere.

1. The B-tree is an ordered access structure, not a copy of the table

Oracle’s default normal index is a balanced B-tree. The top root and any intermediate branch blocks direct a search toward the correct range of leaf blocks. Leaf entries hold ordered key values plus rowids for heap-organized rows. A rowid identifies a row’s physical location sufficiently for Oracle to fetch the table block after locating the key.

Because the tree stays height-balanced as keys are inserted, the existence of deleted space or leaf splits does not imply that a periodic rebuild is required. Rebuild decisions need measured symptoms and a reason, not a calendar.

sql · inspect table rowids and index metadata
SELECT    work_order_id,    status_code,    ROWID AS heap_rowidFROM servicehub_ix_work_orderORDER BY work_order_idFETCH FIRST 5 ROWS ONLY;SELECT    index_name,    blevel,    leaf_blocks,    distinct_keys,    num_rows,    clustering_factor,    last_analyzedFROM user_indexesWHERE table_name = 'SERVICEHUB_IX_WORK_ORDER'ORDER BY index_name;

BLEVEL is the B-tree depth from root to leaves (with zero meaning root and leaf are the same block in the current dictionary definition). LEAF_BLOCKS counts leaf blocks. These statistics are useful evidence, but none by itself means “healthy” or “fragmented.”

2. Access-path names describe how Oracle consumes the index

Access path What Oracle is doing Typical reason
INDEX UNIQUE SCAN Find at most one matching rowid from a unique key Equality on a usable unique/PK index
INDEX RANGE SCAN Walk an ordered key range and return zero or more rowids Range predicate or nonunique equality
INDEX FULL SCAN Read the entire index in key order Index can satisfy columns/order without a start key
INDEX FAST FULL SCAN Read index blocks without preserving key order Index alone can satisfy query; multiblock-style scanning is attractive
INDEX SKIP SCAN Probe logical subindexes of a composite index without its leading column Optimizer finds skip scanning cheaper than alternatives

A full scan and a fast full scan are not synonyms: a normal full index scan preserves index order; a fast full scan does not promise that order. Never remove an ORDER BY because a current plan happens to read an ordered index.

sql · observe estimated access paths without forcing them
EXPLAIN PLAN SET STATEMENT_ID = 'SH08L1_PK'FORSELECT work_order_id, status_codeFROM servicehub_ix_work_orderWHERE work_order_id = 10042;SELECT *FROM TABLE(DBMS_XPLAN.DISPLAY(NULL, 'SH08L1_PK', 'BASIC +PREDICATE'));EXPLAIN PLAN SET STATEMENT_ID = 'SH08L1_RANGE'FORSELECT work_order_id, created_atFROM servicehub_ix_work_orderWHERE created_at >= TIMESTAMP '2026-08-01 00:00:00'  AND created_at <  TIMESTAMP '2026-09-01 00:00:00';SELECT *FROM TABLE(DBMS_XPLAN.DISPLAY(NULL, 'SH08L1_RANGE', 'BASIC +PREDICATE'));

EXPLAIN PLAN shows an estimated plan for teaching. It does not prove an executed cursor used that plan or reveal actual rows. Chapter 09 will separate estimated plans from runtime evidence in depth.

3. Selectivity is necessary reasoning, but not sufficient reasoning

Selectivity is the fraction of rows a predicate is expected to return. A predicate matching one row out of one million is highly selective; one matching half the table is not. Yet cardinality of the indexed column is only one input. The optimizer also considers available statistics, clustering, table/index sizes, required columns, ordering, and the cost of fetching table blocks.

A high-cardinality column can still be a poor access path for a query that requests most rows. Conversely, a lower-cardinality composite index can be excellent when a highly selective combination of predicates is common.

Wrong rule

“High cardinality means create an index” ignores query predicates, projection, table-fetch cost, DML overhead, and the possibility that a full scan is cheaper. Indexes serve workloads, not columns in isolation.

4. Clustering factor measures table correlation, not index fragmentation

Oracle calculates CLUSTERING_FACTOR by walking index entries and observing how often adjacent entries point to different table blocks. A value closer to the number of table blocks means neighboring keys often lead to the same or nearby table blocks; a value closer to the number of rows means the keys point around the heap more randomly. That can make large range scans through the index more expensive because many table blocks must be visited.

The factor is a property of the relationship between one index’s key order and the current heap row placement. Rebuilding the index does not reorder heap rows, so a rebuild is not a general repair for a high clustering factor.

sql · compare index and table statistics
SELECT table_name, num_rows, blocksFROM user_tablesWHERE table_name = 'SERVICEHUB_IX_WORK_ORDER';SELECT    index_name,    distinct_keys,    clustering_factor,    num_rows,    leaf_blocksFROM user_indexesWHERE table_name = 'SERVICEHUB_IX_WORK_ORDER'ORDER BY index_name;

Use the numbers comparatively. If the table has 1,000 data blocks and 100,000 rows, a clustering factor near 1,000 indicates strong physical correlation for that index; near 100,000 indicates weak correlation. There is no universal “bad percentage” that mandates a rebuild.

5. Deliberately wrong approach: rebuild because the clustering factor is high

Suppose SH08_CREATED_IX has a high clustering factor and a date-range report reads many table blocks. Rebuilding only the B-tree can compact or reorganize the index segment, but it does not reorder the heap rows to match created_at. The clustering factor therefore may remain materially unchanged.

sql · capture evidence before any maintenance
SELECT index_name, blevel, leaf_blocks, clustering_factor, last_analyzedFROM user_indexesWHERE index_name = 'SH08_CREATED_IX';SELECT table_name, num_rows, blocks, last_analyzedFROM user_tablesWHERE table_name = 'SERVICEHUB_IX_WORK_ORDER';

A safe repair starts by proving the query is important, gathering representative statistics, comparing full-scan versus index-access cost, and considering whether data organization or partitioning is actually the mechanism needed. Do not reorganize production data merely to improve one clustering factor without evaluating all dependent workloads and recovery/maintenance cost.

6. Hands-on lab: create two distributions and compare evidence

The lab builds 10,000 small rows. One index key follows insertion order; another key deliberately scrambles the mapping to table blocks. Statistics make the difference visible without requiring privileged tracing.

sql · setup and statistics
DROP TABLE servicehub_ix_work_order IF EXISTS PURGE;CREATE TABLE servicehub_ix_work_order (    work_order_id NUMBER PRIMARY KEY,    scrambled_key NUMBER NOT NULL,    status_code   VARCHAR2(12) NOT NULL,    created_at    TIMESTAMP NOT NULL,    payload       VARCHAR2(80));INSERT INTO servicehub_ix_work_orderSELECT    LEVEL,    MOD(LEVEL * 7919, 10000),    CASE MOD(LEVEL,4)      WHEN 0 THEN 'OPEN'      WHEN 1 THEN 'CLOSED'      WHEN 2 THEN 'HOLD'      ELSE 'ASSIGNED'    END,    TIMESTAMP '2026-01-01 00:00:00'      + NUMTODSINTERVAL(LEVEL, 'MINUTE'),    RPAD('x', 40, 'x')FROM dualCONNECT BY LEVEL <= 10000;CREATE INDEX sh08_created_ixON servicehub_ix_work_order(created_at);CREATE INDEX sh08_scrambled_ixON servicehub_ix_work_order(scrambled_key);BEGIN    DBMS_STATS.GATHER_TABLE_STATS(        ownname => USER,        tabname => 'SERVICEHUB_IX_WORK_ORDER',        cascade => TRUE    );END;/SELECT    i.index_name,    i.num_rows,    i.distinct_keys,    i.leaf_blocks,    i.clustering_factor,    t.blocks AS table_blocksFROM user_indexes iJOIN user_tables t ON t.table_name = i.table_nameWHERE i.table_name = 'SERVICEHUB_IX_WORK_ORDER'ORDER BY i.index_name;

You should expect the insertion-correlated CREATED_AT index to have a clustering factor much closer to table-block count than the deliberately scrambled key. Exact numbers vary by block size and storage; the relationship is the evidence.

sql · cleanup
DROP TABLE servicehub_ix_work_order PURGE;

7. Production judgment

Use B-trees when the workload benefits from ordered key lookup, range access, uniqueness enforcement, or index-only access. Measure the fraction of rows retrieved and the table-block work behind rowids. Rebuild an index only for a diagnosed mechanism—never because BLEVEL, free space, or clustering factor “looks high.” Indexes add storage, redo/undo, buffer activity, and DML maintenance on every changed key.

No extra option, pack, restart, or COMPATIBLE change is required for the mandatory Free lab. The next lesson moves from one-column B-trees to design choices that deliberately shape keys: composite order, descending keys, expressions, visibility, and compression.

Check your understanding

  1. What is stored in a normal heap-table B-tree leaf entry besides the key?
  2. Why can a high-cardinality column still be a poor index access path for a particular query?
  3. What semantic difference separates INDEX FULL SCAN from INDEX FAST FULL SCAN?
  4. What does clustering factor measure?
  5. Why does rebuilding an index usually not repair a high clustering factor?
Review the answers

A heap-table B-tree leaf entry includes the indexed key and a rowid pointing to the heap row.

Because the query may retrieve a large fraction of the table, require many table-block visits, or be cheaper as a full scan; cardinality alone is insufficient.

A full index scan preserves key order; a fast full scan reads the index without promising key order.

It measures how closely index-key order correlates with the physical ordering of rows in the heap table.

Rebuilding reorganizes the index segment, not the heap rows whose physical ordering drives the clustering relationship.

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.