Practice Questions — Indexes and Query Performance
← Back to NotesTopic-Specific Questions
Question 1
Easy
What data structure does MySQL use for most indexes?
Balanced tree.
B-TreeQuestion 2
Easy
In InnoDB, what is the clustered index?
The table itself.
The primary key's B-Tree — the table rows ARE the leaves. Data is physically ordered by PK.
Question 3
Easy
What command creates an index on a column?
Standard DDL.
CREATE INDEX idx_name ON table(column);Question 4
Easy
Given an index on (a, b, c), which query CANNOT use it?
Leftmost prefix rule.
WHERE b = 5 AND c = 10 — missing the leading 'a' column.Question 5
Easy
What command shows MySQL's query plan?
Single keyword before the SELECT.
EXPLAINQuestion 6
Medium
In EXPLAIN output, what does type=ALL indicate?
The scanned rows are...
A full table scan — no index used. On large tables this is slow.
Question 7
Medium
What does 'Using index' in EXPLAIN's Extra column mean?
Related to covering index.
The query is served entirely from the index (no row lookup) — a covering index was used.
Question 8
Medium
Why does
WHERE YEAR(date_col) = 2026 often not use an index on date_col?Function on indexed column.
The function YEAR() must be evaluated for every row to check the predicate, which requires scanning every row — defeating the index.
Question 9
Medium
Given a 1M-row table, roughly how many B-Tree disk reads are needed for a PK lookup?
B-Trees are wide.
About 3-4 reads (log_100(1,000,000) ≈ 3).
Question 10
Medium
Does
WHERE name LIKE 'Aar%' use an index on name?Prefix wildcard.
Yes — prefix wildcards are sargable.
Question 11
Medium
Does
WHERE name LIKE '%sharma' use the index?Leading wildcard.
No — leading wildcard prevents B-Tree use.
Question 12
Medium
What is cardinality in SHOW INDEX output?
Distinctness.
The estimated number of distinct values in the column. Higher cardinality = more selective index.
Question 13
Hard
In a composite index on (a, b), does a query
WHERE a = 1 ORDER BY b need a separate sort?B-Trees store values in order.
No. The index already has rows sorted by b within a=1, so MySQL reads them in order.
Question 14
Hard
What is the 'Using filesort' note in EXPLAIN?
It's about sorting.
MySQL is sorting the result set in memory (or on disk if too big), because no index provided the required order. On large result sets this is slow.
Question 15
Hard
If you have indexes on (a), (b), and (a, b), which one is likely redundant?
Leftmost prefix.
The index on (a) alone is usually redundant — (a, b) can serve any query that (a) alone can.
Question 16
Medium
What is a covering index?
All columns needed.
A covering index contains ALL the columns the query needs (both in SELECT and WHERE). MySQL can answer the query entirely from the index without looking up rows. EXPLAIN shows 'Using index' in the Extra column.
Question 17
Hard
Why do indexes slow down writes?
Writes have to maintain consistency.
Every INSERT must add entries to every index. Every UPDATE that changes indexed columns must reposition index entries. Every DELETE must remove index entries. The more indexes, the more work per write. Additionally, index pages must be updated in the buffer pool and eventually on disk, which contributes to I/O.
Question 18
Hard
When is it worth NOT indexing a column that appears in WHERE?
Think about cardinality and table size.
Don't index if: (1) the table is tiny (full scan is already fast), (2) the column has low cardinality AND the query distribution is balanced (e.g., status with 3 equal-frequency values), (3) the query is rare, (4) you have better indexes that already serve the predicate, (5) write performance is critical and the index isn't high-impact on read perf.
Question 19
Easy
Write the SQL to create an index on the email column of a users table.
Standard DDL.
CREATE INDEX idx_users_email ON users(email);Question 20
Medium
Given a common query
WHERE customer_id = ? AND order_date >= ?, write the best composite index.Equality before range.
CREATE INDEX idx_orders_cust_date
ON orders(customer_id, order_date);Question 21
Medium
Rewrite this query to be sargable (index-friendly):
SELECT * FROM orders WHERE MONTH(order_date) = 4 AND YEAR(order_date) = 2026;Convert to a range.
SELECT * FROM orders
WHERE order_date >= '2026-04-01'
AND order_date < '2026-05-01';Question 22
Hard
Given a query
SELECT customer_id, SUM(amount) FROM orders WHERE status='paid' GROUP BY customer_id ORDER BY SUM(amount) DESC LIMIT 10; — propose an index that helps.Filter by status, then group by customer, summing amount.
CREATE INDEX idx_orders_status_cust_amt
ON orders(status, customer_id, amount);Question 23
Medium
Write the command to see all indexes on the 'orders' table.
SHOW command.
SHOW INDEX FROM orders;
-- Or:
SHOW CREATE TABLE orders;Question 24
Hard
Write an EXPLAIN statement for this query and describe what you would look for in the output:
SELECT name FROM users WHERE email = 'aarav@x.com';Check type, key, rows, Extra.
EXPLAIN SELECT name FROM users WHERE email = 'aarav@x.com';Look for: type should be const or ref (unique or regular index hit), key should name your email index, rows should be 1 (or close), Extra should ideally say 'Using index' if the index covers 'name'.
Question 25
Hard
You see 'type=index' in EXPLAIN. Is that good?
Not as good as it sounds.
Not really. type=index means MySQL is doing a FULL index scan — reading every index entry in order. It's better than type=ALL (full table scan) because the index is usually smaller than the table, but it's still a scan. Aim for type=ref, range, eq_ref, or const.
Mixed & Application Questions
Question 1
Easy
If a query is running fast on 100 rows and slow on 1M rows, the likely cause is:
Full-scan cost grows linearly.
A full table scan — missing or unused index.
Question 2
Easy
A unique index does what in addition to speeding up queries?
Constraint.
Enforces uniqueness — duplicate inserts fail with a 1062 error.
Question 3
Medium
Given an index on (a, b), can
WHERE a = 1 ORDER BY b DESC use the index for sorting?Indexes can be scanned backward.
Yes. MySQL reads the index in reverse — 'Backward index scan' — no filesort needed.
Question 4
Medium
You run EXPLAIN and see rows=5,000,000. Is the query guaranteed to scan 5 million rows?
EXPLAIN shows estimates.
No — 'rows' is an estimate based on statistics, not an exact count.
Question 5
Medium
Which is typically faster on a 10M-row table: primary-key lookup or a secondary-index lookup fetching all columns?
PK lookup step.
Primary-key lookup. Secondary-index lookup requires an extra 'PK lookup' step to fetch non-indexed columns.
Question 6
Medium
Drop the index idx_old from the table t.
Two equivalent syntaxes.
DROP INDEX idx_old ON t;
-- or
ALTER TABLE t DROP INDEX idx_old;Question 7
Hard
You have orders(id, customer_id, product_id, amount, status). Queries often filter by (customer_id, status) and (product_id, status). Propose good indexes.
Two composite indexes, each matching a query pattern.
CREATE INDEX idx_orders_cust_status
ON orders(customer_id, status);
CREATE INDEX idx_orders_prod_status
ON orders(product_id, status);Question 8
Hard
Your EXPLAIN output shows 'Using where; Using filesort'. What does it mean and how do you improve?
Filesort = sorting without an index.
The query filters rows (using where) but the ORDER BY requires a separate sort step (filesort) because no index provided the sorted order. Improve by adding an index that covers both the WHERE columns and the ORDER BY column (equality first, then sort column).
Question 9
Hard
Implicit type conversion can kill an index. Explain with an example.
Column type vs parameter type.
If phone is VARCHAR and you query
WHERE phone = 9810012345 (integer), MySQL converts all phone values to integers for comparison — a function on every row. Fix: pass the parameter as a string: WHERE phone = '9810012345'.Question 10
Medium
What's the difference between PRIMARY KEY and UNIQUE INDEX?
Nullability and count.
A table has at most one PRIMARY KEY; PK columns cannot be NULL; in InnoDB the PK defines the clustered index (table order). A table can have many UNIQUE INDEXes; unique index columns CAN be NULL (multiple NULLs are allowed because NULL != NULL). Both enforce uniqueness and speed up lookups.
Question 11
Hard
How does the optimizer choose between two candidate indexes?
Cost-based.
MySQL's optimizer is cost-based. It estimates the number of rows each plan would read (using index cardinality and histograms) and picks the plan with the lowest estimated cost. If stats are stale (table grew, data shifted), it may pick wrong. Run
ANALYZE TABLE to refresh stats, or use FORCE INDEX as a last resort.Multiple Choice Questions
MCQ 1
What data structure does MySQL use for most indexes?
Answer: B
B is correct. Default storage engine InnoDB uses B-Tree indexes for all standard indexes. MEMORY engine offers HASH indexes. FULLTEXT uses inverted indexes.
B is correct. Default storage engine InnoDB uses B-Tree indexes for all standard indexes. MEMORY engine offers HASH indexes. FULLTEXT uses inverted indexes.
MCQ 2
Which command creates an index?
Answer: B
B is correct.
B is correct.
CREATE INDEX name ON table(col) is the SQL-standard syntax. ALTER TABLE t ADD INDEX name (col) is equivalent.MCQ 3
In InnoDB, the primary key defines:
Answer: B
B is correct. Rows ARE the leaves of the PK B-Tree. Secondary indexes reference the PK for row lookups.
B is correct. Rows ARE the leaves of the PK B-Tree. Secondary indexes reference the PK for row lookups.
MCQ 4
Which EXPLAIN 'type' is the worst?
Answer: D
D is correct. ALL = full table scan. The ranking best→worst is: const, eq_ref, ref, range, index, ALL.
D is correct. ALL = full table scan. The ranking best→worst is: const, eq_ref, ref, range, index, ALL.
MCQ 5
Which is the correct composite index for
WHERE a = ? AND b = ? AND c > ??Answer: B
B is correct. Equality columns first (a, b), range column last (c). This order lets the index narrow fast and then range-scan on c.
B is correct. Equality columns first (a, b), range column last (c). This order lets the index narrow fast and then range-scan on c.
MCQ 6
Which query can use a B-Tree index on name?
Answer: B
B is correct. Only prefix LIKE (no leading wildcard) is sargable. Options A, C, D all disable the index.
B is correct. Only prefix LIKE (no leading wildcard) is sargable. Options A, C, D all disable the index.
MCQ 7
What does 'Using index' in EXPLAIN's Extra column indicate?
Answer: B
B is correct. Covering index — no need to access the table itself. Fastest possible read.
B is correct. Covering index — no need to access the table itself. Fastest possible read.
MCQ 8
Which operation in InnoDB is ALWAYS the fastest?
Answer: B
B is correct. PK lookup = 1 B-Tree traversal in the clustered index, fetching the full row directly. No PK lookup step needed.
B is correct. PK lookup = 1 B-Tree traversal in the clustered index, fetching the full row directly. No PK lookup step needed.
MCQ 9
Which query would NOT use an index on (customer_id, order_date)?
Answer: C
C is correct. No leading customer_id — leftmost prefix rule violated, cannot use the index.
C is correct. No leading customer_id — leftmost prefix rule violated, cannot use the index.
MCQ 10
Why might adding too many indexes HURT performance?
Answer: B
B is correct. Writes must maintain all indexes; disk and RAM are wasted on duplicated data.
B is correct. Writes must maintain all indexes; disk and RAM are wasted on duplicated data.
MCQ 11
What does EXPLAIN ANALYZE do (MySQL 8.0.18+)?
Answer: B
B is correct. Regular EXPLAIN shows estimates; EXPLAIN ANALYZE runs the query and returns actuals, exposing estimate errors.
B is correct. Regular EXPLAIN shows estimates; EXPLAIN ANALYZE runs the query and returns actuals, exposing estimate errors.
MCQ 12
Which is the best fix for
WHERE UPPER(email) = 'AARAV@X.COM'?Answer: B
B is correct. Canonicalize on insert. Option A doesn't help because UPPER() on the column still kills sargability. MySQL 8 also supports functional indexes as an alternative, but canonical storage is simpler.
B is correct. Canonicalize on insert. Option A doesn't help because UPPER() on the column still kills sargability. MySQL 8 also supports functional indexes as an alternative, but canonical storage is simpler.
MCQ 13
A query uses 'Using temporary; Using filesort'. What does this suggest?
Answer: B
B is correct. These are both red flags for performance on large result sets. Check if a composite index can serve both the GROUP BY and ORDER BY.
B is correct. These are both red flags for performance on large result sets. Check if a composite index can serve both the GROUP BY and ORDER BY.
MCQ 14
When are HASH indexes (in MEMORY engine) faster than B-Trees?
Answer: B
B is correct. Hash indexes are O(1) for exact matches but useless for ranges and ordering. B-Trees are O(log n) exact and O(log n + k) for ranges — more versatile.
B is correct. Hash indexes are O(1) for exact matches but useless for ranges and ordering. B-Trees are O(log n) exact and O(log n + k) for ranges — more versatile.
MCQ 15
Why does
SELECT * hurt covering-index performance?Answer: B
B is correct. SELECT * forces MySQL to fetch all columns, which usually means a PK lookup even if the filter is served by a secondary index. List only the columns you need to benefit from covering indexes.
B is correct. SELECT * forces MySQL to fetch all columns, which usually means a PK lookup even if the filter is served by a secondary index. List only the columns you need to benefit from covering indexes.
MCQ 16
Which SHOW command lists all indexes on a table?
Answer: B
B is correct.
B is correct.
SHOW INDEX FROM t or SHOW INDEXES FROM t — both work.MCQ 17
Cardinality in SHOW INDEX represents:
Answer: B
B is correct. High cardinality = selective index. Low cardinality = few distinct values (often a poor index choice by itself).
B is correct. High cardinality = selective index. Low cardinality = few distinct values (often a poor index choice by itself).
MCQ 18
Why might the optimizer pick a full scan over an available index?
Answer: B
B is correct. If the optimizer estimates the index will read more than ~20-30% of the table, a sequential scan may actually be faster because of I/O patterns. Stale statistics can also trigger this — ANALYZE TABLE refreshes them.
B is correct. If the optimizer estimates the index will read more than ~20-30% of the table, a sequential scan may actually be faster because of I/O patterns. Stale statistics can also trigger this — ANALYZE TABLE refreshes them.
MCQ 19
Which is the correct way to drop an index?
Answer: A
A is correct.
A is correct.
DROP INDEX idx ON table or ALTER TABLE table DROP INDEX idx.MCQ 20
Which scenario is a GOOD use case for a FULLTEXT index?
Answer: C
C is correct. FULLTEXT indexes are for natural-language search on text columns. They are not used for equality, ranges, or joins.
C is correct. FULLTEXT indexes are for natural-language search on text columns. They are not used for equality, ranges, or joins.
Coding Challenges
Challenge 1: Create and Verify a Simple Index
EasyGiven users(id, name, email), create an index on email, then run EXPLAIN on
SELECT * FROM users WHERE email = 'aarav@example.com' to confirm it is used. Describe what changed.Sample Input
users table with ~100k rows.
Sample Output
EXPLAIN shows type=ref and the new index in 'key' column.
Use CREATE INDEX. Show EXPLAIN output.
CREATE TABLE users (id INT PRIMARY KEY, name VARCHAR(50), email VARCHAR(100));
INSERT INTO users VALUES (1,'Aarav Sharma','aarav@example.com'),
(2,'Priya Nair','priya@example.com');
-- Before index
EXPLAIN SELECT * FROM users WHERE email = 'aarav@example.com';
-- type=ALL, rows=2
CREATE INDEX idx_users_email ON users(email);
-- After index
EXPLAIN SELECT * FROM users WHERE email = 'aarav@example.com';
-- type=ref, key=idx_users_email, rows=1
-- Explanation: B-Tree lookup replaces full scan; on a big table this is 1000x+ faster.Challenge 2: Design a Composite Index
EasyGiven orders(id, customer_id, order_date, amount, status), the app runs
WHERE customer_id = ? AND order_date >= ? frequently. Create the best composite index.Sample Input
No data required — DDL only.
Sample Output
CREATE INDEX statement.
Equality column first, range column second.
CREATE TABLE orders (
id INT PRIMARY KEY,
customer_id INT,
order_date DATE,
amount DECIMAL(10,2),
status VARCHAR(20)
);
CREATE INDEX idx_orders_cust_date
ON orders(customer_id, order_date);
-- Test:
EXPLAIN SELECT * FROM orders
WHERE customer_id = 101 AND order_date >= '2026-01-01';
-- type=range, key=idx_orders_cust_dateChallenge 3: Fix an Anti-Sargable Query
MediumRewrite this query to be index-friendly and explain why it helps:
SELECT * FROM orders WHERE YEAR(order_date) = 2026 AND MONTH(order_date) = 4;Sample Input
orders table with an index on order_date.
Sample Output
Sargable range query that uses the index.
No functions on indexed column.
-- Bad (full scan)
SELECT * FROM orders
WHERE YEAR(order_date) = 2026 AND MONTH(order_date) = 4;
-- Good (range scan on the B-Tree)
SELECT * FROM orders
WHERE order_date >= '2026-04-01'
AND order_date < '2026-05-01';
-- Explanation: the range predicate keeps order_date bare,
-- so the B-Tree on order_date can be traversed directly.
-- The old version forced YEAR() and MONTH() on every row.Challenge 4: Build a Covering Index
MediumThe query
SELECT customer_id, SUM(amount) FROM orders WHERE status='paid' GROUP BY customer_id; is slow. Design a composite index that covers it fully.Sample Input
orders table.
Sample Output
Index with status, customer_id, amount.
Status (equality) first, then customer_id (GROUP BY), then amount (covered).
CREATE INDEX idx_orders_status_cust_amt
ON orders(status, customer_id, amount);
-- EXPLAIN should show 'Using index' in Extra
EXPLAIN
SELECT customer_id, SUM(amount)
FROM orders
WHERE status = 'paid'
GROUP BY customer_id;
-- Expected: type=ref, key=idx_orders_status_cust_amt, Extra='Using index'
-- Fully covering: no row lookup, aggregation happens on the index.Challenge 5: Diagnose via EXPLAIN
MediumGiven a slow query, explain why it's slow (EXPLAIN output shows type=ALL, rows=2,000,000, Extra='Using where; Using filesort'). Propose an index that solves both issues.
Sample Input
SELECT * FROM logs WHERE level = 'ERROR' ORDER BY created_at DESC LIMIT 100;
Sample Output
Diagnosis + index.
Address both the filter and the sort.
-- Diagnosis:
-- type=ALL -> full table scan (no index used for WHERE)
-- rows=2M -> large scan
-- 'Using filesort' -> separate sort step, not index-provided
-- Fix: composite index that both filters (level) and pre-sorts (created_at DESC)
CREATE INDEX idx_logs_level_created
ON logs(level, created_at DESC);
-- After:
-- type=ref, rows='a few thousand', no filesort (backward index scan).
-- Query time drops from seconds to milliseconds.Challenge 6: Drop Redundant Indexes
HardGiven a table with indexes idx_a (a), idx_b (b), idx_ab (a, b), which index is redundant? Write the DROP statement.
Sample Input
No data required.
Sample Output
One DROP INDEX statement with explanation.
Justify the decision.
-- idx_a is redundant: any query on 'a' alone can use idx_ab (leftmost prefix).
-- idx_b stays: needed for queries on b alone.
-- idx_ab stays: needed for queries on (a, b).
DROP INDEX idx_a ON t;
-- Verification:
-- EXPLAIN SELECT * FROM t WHERE a = 5;
-- Should still show type=ref, key=idx_ab
-- Saves disk + speeds up writes to 't'.Challenge 7: FULLTEXT vs B-Tree
HardGiven posts(id, title, body), explain why
WHERE body LIKE '%django%' is slow and rewrite using a FULLTEXT index.Sample Input
posts table with 100k rows.
Sample Output
FULLTEXT index + MATCH AGAINST query.
Use FULLTEXT correctly.
-- Leading wildcard LIKE kills any B-Tree index on body.
-- Full scan examines every row.
-- Fix: FULLTEXT index with natural-language search
CREATE FULLTEXT INDEX idx_posts_body ON posts(body);
-- Query becomes:
SELECT id, title
FROM posts
WHERE MATCH(body) AGAINST('django' IN NATURAL LANGUAGE MODE)
LIMIT 20;
-- EXPLAIN shows type=fulltext.
-- Supports phrase search, relevance ranking, stopword handling.
-- Caveats: min token length (default 4 chars, configurable),
-- InnoDB stopword list, requires rebuilding index after bulk load.Challenge 8: End-to-End Query Tuning
HardYou get a complaint: 'The orders dashboard is slow'. The query:
SELECT customer_id, SUM(amount) AS t FROM orders WHERE status='paid' AND order_date >= '2026-01-01' GROUP BY customer_id ORDER BY t DESC LIMIT 10;. Walk through EXPLAIN, propose the index, and verify.Sample Input
orders(id, customer_id, order_date, amount, status) with 50M rows.
Sample Output
Full diagnosis, index, and verified EXPLAIN.
Explain every step.
-- Step 1: Baseline EXPLAIN
-- type=ALL, rows=50M, Extra='Using where; Using temporary; Using filesort'
-- Dashboard takes 15+ seconds.
-- Step 2: Analyze the query
-- Filter: status + order_date (two columns, mixed equality/range)
-- Group: customer_id
-- Sort: SUM(amount) DESC (can't be served by an index -- aggregate result)
-- Project: customer_id, amount
-- Step 3: Design an index
-- Put equality first (status), then range (order_date). Include customer_id
-- and amount for covering, so aggregation doesn't need row lookups.
CREATE INDEX idx_orders_status_date_cust_amt
ON orders(status, order_date, customer_id, amount);
-- Step 4: Re-run EXPLAIN
-- type=range, key=idx_orders_status_date_cust_amt
-- rows=~500k (filtered down from 50M)
-- Extra='Using where; Using index; Using temporary; Using filesort'
-- The 'Using temporary' and 'Using filesort' remain because the ORDER BY
-- is on the AGGREGATE SUM(amount), not the raw columns -- that's unavoidable.
-- But the 100x reduction in scanned rows takes the query from 15s to ~200ms.
-- Step 5: Verify with EXPLAIN ANALYZE to confirm actual timing.Need to Review the Concepts?
Go back to the detailed notes for this chapter.
Read Chapter NotesWant to master SQL and databases with a mentor?
Explore our MySQL Masterclass