SQL Indexing


SQL Indexing Interview with follow-up questions

1. What is SQL indexing and why is it important?

SQL indexing is the practice of creating auxiliary data structures that allow the database engine to locate rows matching a query condition without scanning the entire table. An index on a column is analogous to a book's index: instead of reading every page to find a topic, you look up the index entry and jump directly to the right page.

Why indexing is important:

Without an index, a query like SELECT * FROM orders WHERE customer_id = 42 requires a full table scan — reading every row. On a table with 50 million rows, this is slow. With an index on customer_id, the engine performs a B-tree lookup in O(log n) time.

What indexes improve:

  • WHERE clause filtering
  • JOIN conditions (especially the join columns)
  • ORDER BY sorting (index may already be ordered)
  • GROUP BY operations
  • Covering queries (index-only scans when all needed columns are in the index)

The trade-off:

  • Indexes speed up reads but slow down writes (INSERT, UPDATE, DELETE must update the index too)
  • Indexes consume disk space
  • Too many indexes on a write-heavy table can hurt overall throughput

Types of indexes:

  • B-tree (default in most databases) — suited for equality and range queries
  • Hash — fast equality lookups; does not support range queries
  • Full-text — searches within text content
  • GIN / GiST (PostgreSQL) — for JSON, arrays, geometric data
  • Clustered — determines physical row order (one per table in SQL Server/MySQL InnoDB)
  • Non-clustered — separate structure with pointers to heap rows
  • Composite — covers multiple columns
  • Partial / filtered — only indexes rows matching a condition
↑ Back to top

Follow-up 1

Can you explain how indexing improves query performance?

Indexing improves query performance by reducing the number of disk I/O operations required to retrieve data. When a query is executed, the database engine can use the index to quickly locate the rows that match the query criteria, instead of scanning the entire table. This reduces the amount of data that needs to be read from disk, resulting in faster query execution.

Follow-up 2

What are the different types of indexes in SQL?

There are several types of indexes in SQL, including:

  1. B-tree index: This is the most common type of index, which stores data in a balanced tree structure. It is efficient for both equality and range queries.

  2. Bitmap index: This type of index uses a bitmap to represent the presence or absence of a value in a column. It is useful for columns with a small number of distinct values.

  3. Hash index: This type of index uses a hash function to map values to index keys. It is efficient for equality queries, but not for range queries.

  4. Full-text index: This type of index is used for searching text-based data, such as documents or web pages. It allows for efficient keyword searches.

Follow-up 3

What are the drawbacks of indexing?

While indexing can greatly improve query performance, it also has some drawbacks:

  1. Increased storage space: Indexes require additional storage space to store the index data structures. This can be a concern for large databases with limited storage capacity.

  2. Increased maintenance overhead: Indexes need to be updated whenever the indexed data is modified, which can add overhead to write operations. This can be a concern for heavily updated tables.

  3. Index fragmentation: Over time, indexes can become fragmented, which can degrade query performance. Regular index maintenance is required to keep indexes optimized.

  4. Increased complexity: Having too many indexes on a table can make the database schema more complex and harder to manage.

Follow-up 4

When should you not use an index?

While indexing can be beneficial for query performance, there are situations where using an index may not be appropriate:

  1. Small tables: For very small tables, the overhead of maintaining an index may outweigh the performance benefits.

  2. Frequent write operations: If a table is heavily updated, the overhead of updating the index on each write operation may outweigh the performance benefits of using the index.

  3. Columns with low selectivity: If a column has a small number of distinct values, using an index may not provide significant performance improvement.

  4. Queries that return a large portion of the table: If a query retrieves a large portion of the table's data, using an index may not be efficient, as it may require scanning the entire index and then accessing the corresponding data pages.

2. How does an index work in SQL?

Most SQL databases use a B-tree (balanced tree) as the underlying data structure for indexes. Understanding the mechanics explains when an index helps and when it doesn't.

B-tree structure:

  • The index stores key values (the indexed column values) in sorted order in a tree structure
  • The tree has a root node, internal nodes (for navigation), and leaf nodes (which hold the actual key values and pointers to the table rows)
  • Because the tree is balanced, every lookup takes O(log n) time regardless of which key is searched

How a query uses an index:

For SELECT * FROM employees WHERE employee_id = 500:

  1. Database starts at the B-tree root
  2. Follows branch pointers (left = smaller, right = larger) down the tree
  3. Reaches the leaf node containing employee_id = 500
  4. Retrieves the row pointer (a page number + row offset, called a "row ID" or "heap pointer")
  5. Jumps directly to that page in the table to fetch the full row

Index-only scans (covering indexes): If the SELECT only needs columns that are all in the index, the database never touches the main table — the index itself contains all the data needed.

CREATE INDEX idx_emp_dept_salary ON employees(department_id, salary);
-- This index covers: SELECT salary FROM employees WHERE department_id = 10

When indexes are NOT used:

  • The optimizer estimates a full scan is cheaper (e.g., query returns >15–20% of rows)
  • A function is applied to the indexed column: WHERE YEAR(hire_date) = 2024 (use WHERE hire_date >= '2024-01-01' AND hire_date < '2025-01-01' instead)
  • Implicit type conversion prevents index use
  • LIKE '%value%' (leading wildcard)
↑ Back to top

Follow-up 1

What is the difference between a clustered and a non-clustered index?

In SQL, a clustered index determines the physical order of data in a table. Each table can have only one clustered index, and it determines the storage order of the table's rows. When a table has a clustered index, the data is physically sorted and stored based on the values of the indexed column(s). This means that the data is stored in the same order as the clustered index, which can improve the performance of queries that retrieve data in the order defined by the index.

On the other hand, a non-clustered index is a separate structure that contains a copy of a portion of the table's data, along with a reference to the location of the full row. Unlike a clustered index, a table can have multiple non-clustered indexes. Non-clustered indexes are typically used to improve the performance of queries that involve filtering, sorting, or joining data based on columns that are not part of the clustered index.

Follow-up 2

How does a database decide which index to use when executing a query?

When executing a query, the database optimizer analyzes the query and the available indexes to determine the most efficient index to use. It takes into account various factors such as the selectivity of the index (how many rows match a particular value), the size of the index, and the cost of accessing the data using the index.

The optimizer uses statistics about the data distribution in the table to estimate the selectivity of the indexes. It then compares the estimated cost of using each index and chooses the one with the lowest cost. The cost is usually based on the number of disk I/O operations required to access the data using the index.

It's important to note that the database optimizer may not always choose the optimal index, especially if the statistics are outdated or if the query is complex. In such cases, it may be necessary to manually specify the index to use or to update the statistics to help the optimizer make better decisions.

Follow-up 3

Can you explain the concept of index cardinality?

Index cardinality refers to the uniqueness or distinctness of values in an index. It represents the number of unique values in the indexed column(s) compared to the total number of rows in the table. A high cardinality means that the index has a large number of unique values, while a low cardinality means that there are relatively few unique values.

The cardinality of an index is an important factor in determining its selectivity and usefulness. A high cardinality index is more selective, as it can narrow down the search to a smaller subset of data. This can result in better query performance, especially when filtering or joining data based on the indexed column(s).

On the other hand, a low cardinality index may not be very selective, as it may have many duplicate values. In such cases, using the index may not significantly improve query performance, as it may still need to scan a large portion of the table to retrieve the desired data.

It's important to consider the cardinality of an index when designing database schemas and choosing the appropriate columns to index.

3. What is a clustered index in SQL?

A clustered index determines the physical storage order of rows in a table. The table data itself is stored sorted according to the clustered index key. Because of this, there can only be one clustered index per table.

Key characteristics:

  • The leaf nodes of the B-tree are the actual table rows (not pointers to rows)
  • Lookups by the clustered index key are extremely fast — no extra row pointer lookup needed
  • Range queries on the clustered index key are especially efficient because data is physically contiguous on disk

Default behavior by database:

  • MySQL (InnoDB): The primary key is always the clustered index. If no primary key is defined, InnoDB uses the first UNIQUE NOT NULL column, or generates a hidden 6-byte row ID.
  • SQL Server: A primary key creates a clustered index by default (you can override this)
  • PostgreSQL: PostgreSQL does not have a true clustered index; it uses heap storage. The CLUSTER command physically reorders the table once, but it is not maintained automatically.

Example:

-- SQL Server: primary key = clustered index by default
CREATE TABLE orders (
    order_id   INT PRIMARY KEY,         -- clustered index on order_id
    order_date DATE,
    total      DECIMAL(10,2)
);

-- Explicit clustered index on SQL Server
CREATE CLUSTERED INDEX idx_order_date ON orders(order_date);

Choosing the clustered index key: Ideal characteristics:

  • Narrow — shorter keys mean more entries fit per B-tree page
  • Static — key value should not change (changing it requires physically moving the row)
  • Monotonically increasing — sequential inserts (e.g., auto-increment IDs) avoid page splits; random values (e.g., UUID v4) cause fragmentation
↑ Back to top

Follow-up 1

How does a clustered index affect the physical order of data?

A clustered index determines the physical order of data in a table. The data is physically sorted and stored on disk based on the values of the indexed column(s) in ascending or descending order. This means that the data in a table with a clustered index is physically stored in the same order as the clustered index key values.

Follow-up 2

Can a table have more than one clustered index?

No, a table can have only one clustered index. The clustered index determines the physical order of data in a table, so having multiple clustered indexes would create conflicts in determining the physical order.

Follow-up 3

What are the advantages and disadvantages of using a clustered index?

Advantages of using a clustered index:

  • Improved query performance for range-based queries that match the clustered index key
  • Elimination of the need for a separate index structure, as the clustered index itself serves as the data structure
  • Efficient retrieval of data in the order defined by the clustered index

Disadvantages of using a clustered index:

  • Slower performance for insert, update, and delete operations, as the physical order of data needs to be maintained
  • Increased storage requirements, as the data is physically sorted and stored based on the clustered index key values
  • Limited flexibility in choosing the clustering key, as it should be unique and stable

4. What is a non-clustered index in SQL?

A non-clustered index is a separate data structure from the table's row data. It stores a copy of the indexed column values in sorted order, with each entry containing a pointer back to the actual row in the table (a row ID or primary key reference).

Key characteristics:

  • Multiple non-clustered indexes can exist on one table (SQL Server allows up to 999)
  • The leaf nodes contain the indexed key values + a row locator (not the full row data)
  • Lookups require two steps: find the key in the index, then follow the pointer to the table row (called a key lookup or bookmark lookup)
  • The extra pointer lookup adds overhead — index-only scans (covering indexes) avoid this

Structure:

Non-clustered index B-tree
└── Leaf: (email value, row pointer → heap/clustered index)

Separate table heap or clustered index
└── Full row data

Creating a non-clustered index:

CREATE INDEX idx_employees_email ON employees(email);
CREATE NONCLUSTERED INDEX idx_emp_dept ON employees(department_id);  -- SQL Server explicit syntax

Covering index (important optimization): Include additional columns in the index so queries can be satisfied without touching the main table:

CREATE INDEX idx_emp_dept_covering
ON employees(department_id)
INCLUDE (first_name, salary);
-- SELECT first_name, salary FROM employees WHERE department_id = 5
-- can be answered entirely from the index

Non-clustered vs clustered:

Aspect Clustered Non-Clustered
Number per table 1 Many
Data storage Rows stored in index Separate structure with pointers
Row lookup Direct (leaf = row) Needs pointer dereference
Range query performance Best Good but extra lookup
↑ Back to top

Follow-up 1

How does a non-clustered index differ from a clustered index?

A non-clustered index differs from a clustered index in the way it organizes and stores data. In a clustered index, the data rows are physically sorted and stored in the order of the indexed column(s). This means that a table can have only one clustered index. On the other hand, a non-clustered index does not affect the physical order of the data rows. It is stored separately and contains a copy of the indexed columns along with a pointer to the actual data rows. A table can have multiple non-clustered indexes.

Follow-up 2

How many non-clustered indexes can a table have?

In SQL Server, a table can have up to 999 non-clustered indexes. However, it is important to note that having too many indexes on a table can negatively impact performance, as each index requires additional storage space and maintenance overhead.

Follow-up 3

What are the advantages and disadvantages of using a non-clustered index?

Advantages of using a non-clustered index include faster data retrieval for queries that involve the indexed columns, improved query performance for filtering and sorting operations, and the ability to cover queries by including all the required columns in the index. Disadvantages of using a non-clustered index include increased storage space requirements, additional maintenance overhead for index updates, and potential performance degradation for insert, update, and delete operations on the indexed columns.

5. What is a composite index in SQL?

A composite index (also called a multi-column index or compound index) is an index built on two or more columns of a table. The index entries are sorted first by the first column, then by the second within each first-column value, and so on.

CREATE INDEX idx_emp_dept_salary ON employees(department_id, salary);
CREATE INDEX idx_name_composite ON customers(last_name, first_name, city);

The left-prefix rule — the most important concept:

A composite index on (A, B, C) supports queries filtering on:

  • A alone ✓
  • A, B
  • A, B, C
  • B alone ✗ (cannot use the index without the leftmost column)
  • B, C alone ✗
-- index on (department_id, salary):
WHERE department_id = 5                          -- uses index ✓
WHERE department_id = 5 AND salary > 80000       -- uses index fully ✓
WHERE salary > 80000                             -- cannot use this index ✗

When composite indexes beat separate single-column indexes:

A single composite index can serve multiple columns in a single B-tree traversal. Two separate single-column indexes would require a merge or only one would be used.

Covering queries: A composite index covering all columns in a SELECT enables an index-only scan — the database never reads the main table:

CREATE INDEX idx_covering ON orders(customer_id, order_date, total);
SELECT order_date, total FROM orders WHERE customer_id = 42;
-- answered entirely from the index

Column order matters: Put the most selective column first for equality filters; put range-queried columns last (range stops the index from being used for subsequent columns).

↑ Back to top

Follow-up 1

How does a composite index differ from a single-column index?

A composite index differs from a single-column index in that it is created on multiple columns instead of just one. This means that the index is built using a combination of values from multiple columns, allowing for more specific and targeted searches. In contrast, a single-column index is created on a single column and can only be used to search or sort based on the values in that specific column.

Follow-up 2

When would you use a composite index?

A composite index is typically used when there is a need to search or sort data based on multiple columns. It is especially useful when queries involve conditions or sorting on multiple columns simultaneously. By creating a composite index on these columns, the database can optimize the query execution and improve performance.

Follow-up 3

What are the advantages and disadvantages of using a composite index?

Advantages of using a composite index include improved query performance for queries involving multiple columns, reduced disk space usage compared to creating separate indexes for each column, and better index utilization for certain types of queries.

However, there are also some disadvantages to consider. Creating and maintaining a composite index can be more complex and time-consuming compared to a single-column index. Additionally, the composite index may not be as effective for queries that only involve one of the indexed columns, as the index is optimized for searches and sorts involving all the indexed columns.

6. What is a covering index and how does it improve query performance?

A covering index is an index that contains all the columns a query needs to satisfy — so the database engine can answer the query entirely from the index without reading the main table (called an index-only scan).

-- Query:
SELECT first_name, salary FROM employees WHERE department_id = 5;

-- Covering index includes all three columns touched by the query:
CREATE INDEX idx_emp_covering ON employees(department_id, first_name, salary);
-- department_id is the filter; first_name and salary are the SELECT columns

Without a covering index, the engine must: (1) look up matching rows in the index, then (2) fetch the full row from the table heap for each match. With a covering index, step 2 is eliminated.

How to create one: In PostgreSQL and SQL Server, use INCLUDE to add non-key columns to the index leaf:

CREATE INDEX idx_emp_dept ON employees(department_id) INCLUDE (first_name, salary);

When to use: High-frequency queries on large tables where the select list and filter columns are known and stable. Covering indexes use more disk space and slow down writes — use them selectively.


↑ Back to top

7. What are window functions in SQL and when would you use them?

Window functions perform calculations across a set of rows related to the current row, without collapsing the rows into a single group (unlike GROUP BY aggregates). They use an OVER() clause.

SELECT
    name,
    department,
    salary,
    AVG(salary)  OVER (PARTITION BY department) AS dept_avg,
    RANK()       OVER (PARTITION BY department ORDER BY salary DESC) AS dept_rank,
    LAG(salary)  OVER (ORDER BY hire_date) AS prev_hire_salary
FROM employees;

Key window function categories:

  • Ranking: ROW_NUMBER(), RANK(), DENSE_RANK(), NTILE(n)
  • Offset: LAG(col, n), LEAD(col, n), FIRST_VALUE(), LAST_VALUE()
  • Aggregate: SUM(), AVG(), COUNT(), MIN(), MAX() — used with OVER()

RANK() vs ROW_NUMBER() vs DENSE_RANK(): With scores 100, 100, 90:

  • ROW_NUMBER(): 1, 2, 3 (always unique)
  • RANK(): 1, 1, 3 (ties share rank, gap after)
  • DENSE_RANK(): 1, 1, 2 (ties share rank, no gap)

Common interview use: "Find the top 3 earners per department":

WITH ranked AS (
    SELECT name, department, salary,
           ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) AS rn
    FROM employees
)
SELECT * FROM ranked WHERE rn <= 3;

Supported in: PostgreSQL, MySQL 8.0+, SQL Server, Oracle, SQLite 3.25+.


↑ Back to top

Live mock interview

Mock interview: SQL Indexing

Intermediate ~5 min Your own free AI key

Your voice and your AI key never touch our servers; the key stays in this browser and is sent only to Google. Only your round scores are saved to track progress.