Chapter 11 · Index-Aware Logical and Physical Design

Covering, Partial, and Specialized Indexes

Use covering, partial, expression, and specialized indexes appropriately, understanding what each technique optimizes and what tradeoffs it introduces.

Beginner70–100 minutesAdvanced index patternsLast reviewed: August 2026

Learning outcomes

Ordinary B-tree indexes are only the beginning. Production databases often use covering indexes, partial indexes, expression indexes, and engine-specific specialized index types to align physical access structures with particular workloads. These techniques can be powerful, but each should solve a concrete problem.

01

Use covering indexes to reduce table lookups for narrow hot queries.

02

Use partial indexes to target small operational subsets.

03

Use expression indexes when predicates depend on computed expressions.

04

Recognize when full-text, spatial, JSON, or other specialized indexes are appropriate.

Covering index concept

An index “covers” a query when the engine can satisfy required predicates and selected columns from the index alone, avoiding or reducing visits to the base table.

Example

sql · example
SELECT opened_at, status_codeFROM work_orderWHERE asset_id = ?ORDER BY opened_at DESCLIMIT 20;

An index containing:

model · example
asset_id, opened_at, status_code

may allow an index-only plan under suitable engine/visibility conditions.

Key columns versus included columns

Some databases support INCLUDE columns that are stored in leaf entries but do not participate in index ordering:

sql · example
CREATE INDEX ...ON work_order(asset_id, opened_at)INCLUDE (status_code);

This can cover a query without widening the ordered key unnecessarily.

Covering is workload-specific

Adding every selected column to an index is usually a mistake. Cover only narrow, frequent, latency-sensitive queries where avoiding table access materially helps.

Partial indexes

A partial index stores only rows matching a predicate:

sql · example
CREATE INDEX ix_work_order_openON work_order(opened_at)WHERE status_code IN ('open','blocked','scheduled');

This can be much smaller than indexing all WorkOrders when active rows are a small fraction.

Partial indexes can enforce conditional uniqueness

Example concept:

model · example
one active primary assignment per work order

A unique partial index may enforce that invariant:

model · example
CREATE UNIQUE INDEX ...ON work_order_assignment(work_order_id)WHERE role_code = 'primary'  AND ended_at IS NULL;

Exact syntax and predicate support vary by database engine.

Dual value

A specialized index can sometimes improve performance and encode a business rule at the same time.

Expression indexes

For case-insensitive lookup:

model · example
WHERE lower(email) = lower(:email)

an index on:

model · example
lower(email)

may align with the predicate.

Normalized forms in expression indexes

Instead of repeatedly computing normalized identifiers in application code, some systems index a canonical expression such as:

model · example
lower(trim(sku))

But normalization rules should be stable and deterministic.

Full-text indexes

Searching:

model · example
WHERE description LIKE '%bearing noise%'

is usually not what ordinary B-tree indexes are designed for. Full-text search indexes tokenize and rank text according to engine-specific capabilities.

Spatial indexes

Geographic queries such as “find service centers within 25 km” need spatial data types and spatial indexes rather than ordinary scalar B-tree assumptions.

JSON/document indexes

If WorkshopHub stores semi-structured diagnostic metadata in JSON, some engines support GIN/inverted or path indexes over JSON contents. Use them only when JSON is justified by the logical model and queried frequently.

Hash indexes

Some engines offer hash index structures optimized for equality. Capabilities and durability/support vary. B-tree remains the general-purpose default in many systems because it supports equality, range, and ordering.

Bitmap-like strategies

Analytical engines and warehouses may use bitmap indexes or bitmap execution techniques for low-cardinality columns. These are workload/engine-specific and can be inappropriate for high-concurrency OLTP writes.

BRIN-style indexes

Block-range indexes summarize physical ranges and can be extremely compact for naturally ordered large tables, such as append-heavy event tables by timestamp. They are less precise than B-trees but much smaller.

Clustering and index-organized storage

Some systems can physically cluster rows by an index or store the table itself in a primary-key/index-organized structure. This can improve locality for one access path but usually cannot keep the table physically clustered by many different indexes simultaneously.

Covering-index tradeoff

A wider covering index may:

  • reduce table reads;
  • increase storage;
  • increase write amplification;
  • reduce cache density;
  • take longer to build/rebuild.

Partial-index tradeoff

Partial indexes are smaller and targeted but useful only when query predicates logically imply the index predicate. Query formulation matters.

WorkshopHub advanced candidates

NeedIndex pattern
Active work queuePartial index on active statuses
Case-insensitive customer emailExpression index on normalized email
Newest orders by asset with status displayCovering composite index
Search problem descriptionsFull-text index
Large append-only audit table by timeBRIN/range-summary style index where supported

Practice: choose the specialized index

Four needs

  1. Search part descriptions by words.
  2. Query only the 2% of WorkOrders still active.
  3. Lookup email case-insensitively.
  4. Return asset_id/opened_at/status without touching table pages.
Review answer

Full-text index; partial index; expression index; covering/index-only-oriented composite index. Exact syntax depends on the DBMS.

Summary and next lesson

Advanced index types let physical design match specialized workloads: covering indexes reduce table access, partial indexes target small subsets, expression indexes support computed predicates, and specialized structures serve text, spatial, JSON, or append-heavy data. The final lesson examines their cost over the full database lifecycle.

References

  • PostgreSQL documentation on index-only scans, partial indexes, expression indexes, GIN, GiST, and BRIN.
  • Markus Winand, SQL Performance Explained.
  • Database engine documentation for supported specialized index types.

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 assets compatible with the Ethereum/ERC-20 network. Do not send TRC-20/TRON assets.