Tutorials Logic, IN info@tutorialslogic.com

DBMS Indexing B tree, B+ tree, Hash Index

Index Access Paths

An index is an ordered or hashed access structure that helps the optimizer locate candidate rows without reading every table page. This lesson is for learners who can write SELECT queries and want to understand why an index helps one predicate, is ignored for another, or makes writes more expensive.

You will learn to choose column order, recognize selective and nonselective predicates, read an execution plan, and remove indexes whose maintenance cost is not justified by measured workload benefit.

Why Indexing?

Without an index, a database must scan every row in a table to find matching records (full table scan). For large tables with millions of rows, this is extremely slow. An index is a data structure that allows the database to find rows much faster - similar to a book's index that lets you jump directly to a topic instead of reading every page.

Trade-offs:

  • Indexes speed up SELECT queries (reads)
  • Indexes slow down INSERT, UPDATE, DELETE (writes) because the index must be updated
  • Indexes consume additional disk space

Dense vs Sparse Index

Type Description Pros Cons
Dense Index An index entry for every record in the data file Faster search (direct lookup) More storage space
Sparse Index Index entries only for some records (e.g., one per block) Less storage space Requires sequential scan within block

Types of Indexes

Index Type Description Use Case
Primary Index Built on the primary key. Data file is ordered by the key. One entry per block (sparse). Fast lookup by primary key
Secondary Index Built on non-primary key attributes. Data file may not be ordered by this key. Dense index. Fast lookup by non-key attributes
Clustering Index Built on a non-key attribute that orders the data file. One entry per distinct value. Range queries on ordered non-key fields
Unique Index Ensures all values in the indexed column are unique. Enforce uniqueness on non-PK columns
Composite Index Index on multiple columns. Queries filtering on multiple columns
Full-Text Index Optimized for text search operations. Language-aware word and phrase search

B-Tree and B+ Tree Index

The most common index structure in relational databases is the B+ Tree:

Why B+ Tree is preferred:

  • B-Tree: A balanced tree where each node can have multiple keys and children. Data pointers exist at all levels (internal and leaf nodes).
  • B+ Tree: An enhanced B-Tree where: All data pointers are stored only in leaf nodes
  • Internal nodes contain only keys (for routing)
  • Leaf nodes are linked in a doubly-linked list (enables efficient range queries)
  • All leaf nodes are at the same level (balanced)
  • Range queries are efficient (traverse linked leaf nodes)
  • Internal nodes can hold more keys (no data pointers), so the tree is shorter
  • Fewer disk I/Os for most queries
  • Used by MySQL (InnoDB), PostgreSQL, Oracle, SQL Server

Hash Index

A hash index uses a hash function to map key values to bucket addresses. It provides O(1) average-case lookup for equality queries.

  • Pros: Very fast for equality searches (WHERE id = 5)
  • Cons: Cannot support range queries (WHERE id > 5), ordering, or LIKE queries
  • Use case: Hash joins, in-memory tables (MySQL MEMORY engine)

Bitmap Index

A bitmap index uses a bit array (bitmap) for each distinct value of an attribute. Each bit represents whether a row has that value.

  • Best for: Low-cardinality columns (few distinct values) like Gender (M/F), Status (Active/Inactive)
  • Pros: Very compact, fast for AND/OR operations
  • Cons: Poor for high-cardinality columns, expensive to update
  • Used mainly in analytical systems; some engines instead build temporary bitmap operations from ordinary indexes during query execution

Index Best Practices

  • Index columns used frequently in WHERE, JOIN, and ORDER BY clauses
  • Avoid indexing columns with very low cardinality (e.g., boolean columns)
  • Use composite indexes for queries that filter on multiple columns
  • Order composite columns around real predicates: leading columns usually determine which prefix searches and orderings are efficient
  • Don't over-index - each index slows down writes
  • Use EXPLAIN/EXPLAIN ANALYZE to check if queries use indexes

Selectivity and Cardinality

Cardinality is the number of distinct values in a column or column combination. Selectivity describes how narrowly a predicate filters the table. A unique email lookup is highly selective because it should return at most one row; a status = active predicate may be weakly selective when almost every row is active.

An index lookup has startup work and often requires extra table-page reads. When a predicate returns a large fraction of the table, sequentially reading table pages can cost less than jumping between an index and many scattered rows. The optimizer estimates that crossover from statistics, data distribution, row width, and storage costs. An existing index is therefore an option, not an instruction.

Skewed Values

A single distinct-count value cannot describe skew. If 99 percent of orders are complete and 1 percent are pending, the same status index may be useful for pending but wasteful for complete. Histograms or frequency statistics help the optimizer distinguish those values. Refresh statistics after major data changes when estimates no longer resemble actual row counts.

Composite Index Order

A composite B-tree index is sorted lexicographically: first by its leading column, then by the next column within equal leading values. An index on (customer_id, ordered_at) can efficiently find one customer and then scan that customer's date range. A date-only query may not have a contiguous starting range because every customer owns a separate date sequence.

The useful-prefix rule is more precise than saying every query must filter the first column. Some engines can scan the whole index, use skip-scan-like strategies, or use it for covering data, but those alternatives may be costly. Choose order from the workload: equality columns commonly lead, followed by range or ordering columns, while selectivity and sort requirements can change the decision.

Customer Order Index

Customer Order Index
CREATE INDEX idx_orders_customer_date
    ON orders (customer_id, ordered_at);

SELECT order_id, ordered_at, total_amount
FROM orders
WHERE customer_id = 42
  AND ordered_at >= '2026-01-01'
ORDER BY ordered_at;
Output
The index provides a narrow customer range already ordered by ordered_at; the exact plan remains optimizer- and data-dependent.

Covering and Filtered Indexes

A covering index contains every column needed by a query, allowing an index-only access path when the engine can confirm row visibility without fetching the base row. Key columns control search order; included or payload columns, where supported, can cover output without changing that order. Wider indexes consume more storage and create more write and cache pressure, so coverage should serve an important repeated query rather than every possible SELECT list.

A partial or filtered index stores only rows satisfying a predicate, such as open orders. It can be smaller and more selective than indexing the entire status column, but the query predicate must imply the index condition. Product syntax and matching rules differ, so inspect the actual plan instead of assuming the index qualifies.

Sargable Predicates

A search argument is sargable when the engine can translate it into an index range. Wrapping an indexed column in a function, applying an implicit type conversion, or beginning a pattern with a wildcard can hide a useful range. For example, comparing DATE(created_at) to one day often prevents a normal index range; comparing created_at with a start timestamp and the next day's timestamp exposes clear lower and upper bounds.

Expose a Timestamp Range

Expose a Timestamp Range
-- Harder to use with a normal index on created_at
WHERE DATE(created_at) = '2026-07-13'

-- Sargable half-open range
WHERE created_at >= '2026-07-13 00:00:00'
  AND created_at <  '2026-07-14 00:00:00'

Measure the Plan

Start with the slow query and representative parameters, not with a guessed index. Capture its plan, estimated rows, actual rows when safe, elapsed time, logical reads, sort or hash spills, and rows removed by filters. A large estimate-versus-actual mismatch points toward stale statistics, correlated columns, parameter sensitivity, or a predicate the estimator cannot model well.

After adding an index, repeat the same measurement under realistic cache and concurrency conditions. Check write latency and storage growth as well as read speed. Remove redundant indexes only after comparing key order, uniqueness, included columns, constraints, and workload use; two similarly named indexes may enforce different guarantees.

Plan Signal Likely Meaning Next Check
Table scan with few output rows No useful access path or hidden predicate Sargability, type conversion, candidate index
Index lookup with many table fetches Predicate is not selective or index does not cover Returned fraction and covering trade-off
Large estimate error Statistics or data correlation problem Refresh and inspect distribution
Sort despite an index Index order differs from requested order Equality prefix and sort direction
Fast reads but slow writes Too many or overly wide indexes Usage statistics and write workload

DBMS SQL lab setup

DBMS SQL lab setup
CREATE TABLE lesson_dbms (
    id INT PRIMARY KEY,
    description VARCHAR(120),
    amount DECIMAL(10,2),
    status VARCHAR(20)
);

INSERT INTO lesson_dbms VALUES
(1, 'DBMS normal case', 1000.00, 'active'),
(2, 'DBMS boundary case', 0.00, 'review');

SELECT * FROM lesson_dbms;

DBMS reasoning query

DBMS reasoning query
BEGIN;
UPDATE lesson_dbms
SET status = 'checked'
WHERE amount >= 0;

SELECT status, COUNT(*) AS rows_seen
FROM lesson_dbms
GROUP BY status;
ROLLBACK;

-- Explanation: ROLLBACK lets you test the concept safely before committing changes.
Before you move on

DBMS Indexing B tree, B+ tree, Hash Index Mastery Check

4 checks
  • Without an index, a database must scan every row in a table to find matching records (full table scan).
  • For large tables with millions of rows, this is extremely slow.
  • A hash index uses a hash function to map key values to bucket addresses.
  • It provides O(1) average-case lookup for equality queries.

DBMS Questions Learners Ask

Every index is another data structure the DBMS must maintain. An INSERT adds entries to each relevant index, while an UPDATE may remove and add entries when indexed columns change.

B-tree composite indexes are most effective from their leftmost columns. An index ordered by department_id and then salary groups salaries inside each department, so a salary-only filter may not have a useful starting point.

The optimizer estimates whether using the index costs less than scanning the table. It may prefer a scan when the query returns a large percentage of rows, statistics are stale, a function hides the indexed column, types require conversion, or the index order does not match the predicate.

Browse Free Tutorials

Explore 500+ free tutorials across 20+ languages and frameworks.