MySQL Interview Questions
This page covers 25+ of the most commonly asked MySQL interview questions with thorough, production-accurate answers. Questions are ordered roughly from fundamental to advanced.
Q1: What is the difference between CHAR and VARCHAR?
Feature | CHAR | VARCHAR |
|---|---|---|
Storage | Fixed length — always uses declared length | Variable length — uses actual length + 1-2 bytes overhead |
Trailing spaces | Padded with spaces; stripped on retrieval | Not padded; preserved exactly |
Performance | Slightly faster for fixed-width data | Better for variable-width data |
Best use case | Fixed-width codes: country codes, status flags | Names, emails, addresses — anything variable |
CHAR(10) -- always stores 10 bytes VARCHAR(10) -- stores 1-10 bytes + 1 byte for length
Q2: What is the difference between DELETE, TRUNCATE, and DROP?
Feature | DELETE | TRUNCATE | DROP |
|---|---|---|---|
Type | DML | DDL | DDL |
Can filter rows | Yes — WHERE clause | No — removes all rows | Removes entire table |
Rollbackable | Yes (in a transaction) | No (implicit commit) | No (implicit commit) |
AUTO_INCREMENT reset | No | Yes — resets to 1 | Table is gone |
Triggers fired | Yes | No | No |
Speed | Slow for large tables | Very fast (deallocates pages) | Instantaneous |
Q3: Explain ACID Properties
ACID defines the guarantees a transaction must provide:
Atomicity — a transaction is all-or-nothing. If any statement fails, all changes are rolled back. Either the entire transfer completes, or nothing changes.
Consistency — a transaction brings the database from one valid state to another. Constraints (FK, UNIQUE, NOT NULL) are always enforced.
Isolation — concurrent transactions do not see each other's intermediate state. The degree is controlled by the isolation level (READ COMMITTED, REPEATABLE READ, etc.).
Durability — once a transaction commits, the changes survive crashes. InnoDB achieves this via the redo log flushed to disk before commit.
Q4: What are the different types of JOINs?
Join | Returns |
|---|---|
INNER JOIN | Only rows with matches in both tables. |
LEFT JOIN (LEFT OUTER JOIN) | All rows from left table; NULL columns for right where no match. |
RIGHT JOIN (RIGHT OUTER JOIN) | All rows from right table; NULL columns for left where no match. |
CROSS JOIN | Cartesian product — every combination of left and right rows. |
SELF JOIN | A table joined to itself, using two aliases (e.g., employees and managers). |
FULL OUTER JOIN | All rows from both tables; NULL where no match. MySQL simulates this with LEFT JOIN UNION RIGHT JOIN. |
Q5: What is normalization? Explain 1NF, 2NF, 3NF.
Normalization is the process of organizing a relational database to reduce data redundancy and improve data integrity.
1NF (First Normal Form) — each column holds atomic (indivisible) values; no repeating groups. No storing a comma-separated list in a single column.
2NF (Second Normal Form) — satisfies 1NF, and every non-key column is fully dependent on the entire primary key (eliminates partial dependencies in composite-key tables).
3NF (Third Normal Form) — satisfies 2NF, and no non-key column depends on another non-key column (eliminates transitive dependencies). E.g., storing both zip_code and city in a users table violates 3NF because city depends on zip_code, not the user ID.
Q6: What is an index? How does a B-tree index work?
An index is a separate data structure that allows MySQL to find rows quickly without scanning every row in the table.
A B-tree index (the default for InnoDB) is a balanced tree where:
- Leaf nodes contain the indexed key values and pointers to the actual rows.
- Internal nodes contain key ranges that guide the search down the tree.
- The tree is always balanced — lookups, inserts, and deletes take O(log n) time.
An equality lookup like WHERE id = 42 descends from the root to a leaf in a few operations, regardless of table size.
Q7: What is the difference between clustered and non-clustered indexes?
Feature | Clustered (InnoDB Primary Key) | Non-Clustered (Secondary Index) |
|---|---|---|
Data storage | Table data IS the index — stored in PK order | Separate structure; leaf nodes contain PK value |
Lookup cost | Direct — data is at the leaf | Two-step: find PK in secondary index, then look up in clustered index |
Count per table | Exactly one | Unlimited |
Row order | Rows physically ordered by PK | No effect on physical row order |
Q8: What is a view? Is it always updatable?
A view is a stored SELECT query that acts like a virtual table. It does not store data itself — every query against the view executes the underlying SELECT.
A view is updatable (you can INSERT/UPDATE/DELETE through it) only if:
It references exactly one base table.
It does not use DISTINCT, GROUP BY, HAVING, UNION, or aggregate functions.
It does not use subqueries in the SELECT list.
The view includes all NOT NULL columns with no default.
Views that aggregate data (e.g., COUNT, SUM) or join multiple tables are read-only.
Q9: What is a stored procedure vs a function?
Feature | Stored Procedure | Function |
|---|---|---|
Called with | CALL proc_name() | Used in expressions: SELECT func_name() |
Return value | Zero or more via OUT parameters or result sets | Exactly one scalar value via RETURN |
Can have side effects | Yes — can INSERT/UPDATE/DELETE | Traditionally not (deterministic expected); MySQL allows it but is discouraged |
Use in SQL | Cannot be used in SELECT, WHERE, etc. | Can be used anywhere an expression is valid |
Transaction control | Can issue COMMIT/ROLLBACK | Cannot |
Q10: What is a trigger?
A trigger is a stored program that automatically executes in response to a DML event (INSERT, UPDATE, or DELETE) on a table. Triggers fire BEFORE or AFTER the event.
Common uses: audit logging, enforcing complex business rules, maintaining denormalized columns.
-- Automatically set updated_at on every UPDATE CREATE TRIGGER trg_users_updated BEFORE UPDATE ON users FOR EACH ROW SET NEW.updated_at = NOW();
Q11: Explain transaction isolation levels.
Level | Dirty Read | Non-Repeatable Read | Phantom Read | InnoDB Default? |
|---|---|---|---|---|
READ UNCOMMITTED | Possible | Possible | Possible | No |
READ COMMITTED | Not possible | Possible | Possible | No |
REPEATABLE READ | Not possible | Not possible | Possible (mostly prevented by InnoDB) | Yes |
SERIALIZABLE | Not possible | Not possible | Not possible | No |
- Dirty read — reading uncommitted changes from another transaction.
- Non-repeatable read — reading the same row twice and getting different values because another transaction committed in between.
- Phantom read — a query returns different rows on two executions because another transaction inserted/deleted rows matching the WHERE clause.
Q12: What is a deadlock? How do you prevent it?
A deadlock occurs when two transactions each hold a lock the other needs, causing both to wait indefinitely. MySQL detects deadlocks automatically and rolls back the cheaper transaction.
Prevention strategies:
Always acquire locks in the same order across all transactions.
Keep transactions short to minimize lock-hold time.
Use SELECT ... FOR UPDATE only when you intend to update the row.
Index columns used in WHERE and JOIN — unindexed joins can lock far more rows than expected.
Reduce transaction scope — avoid doing non-DB work (HTTP calls) inside a transaction.
Q13: What is the difference between InnoDB and MyISAM?
Feature | InnoDB | MyISAM |
|---|---|---|
Transactions | Yes | No |
Foreign keys | Yes | No |
Locking | Row-level | Table-level |
Crash recovery | Automatic via redo log | Manual REPAIR TABLE needed |
Full-text search | Yes (MySQL 5.6+) | Yes (legacy) |
Recommendation | Always use for new tables | Avoid — legacy only |
Q14: What is EXPLAIN used for?
EXPLAIN shows the query execution plan — how MySQL intends to execute a query. Key things to look for:
type: ALL means a full table scan — needs an index.
key: NULL means no index is used.
rows: shows the estimated number of rows examined — lower is better.
Extra: Using filesort or Using temporary indicate expensive sorting/grouping.
Q15: How would you optimize a slow query?
Run EXPLAIN and identify the access type, key used, and rows examined.
Add an index on the column(s) in the WHERE clause if type is ALL.
Use a composite index if filtering on multiple columns together.
Avoid functions on indexed columns in WHERE: write WHERE created_at > x instead of WHERE YEAR(created_at) = x.
Select only needed columns — avoid SELECT *.
Break a large query into smaller steps using CTEs for readability and optimizer hints.
Use EXPLAIN ANALYZE (MySQL 8.0+) to compare estimates against actual row counts.
Check if a covering index can serve the query without touching the table.
Q16: What is SQL injection? How do you prevent it?
SQL injection is an attack where user input is embedded directly into a SQL query, allowing the attacker to alter the query's logic. A classic example is a login bypass using admin' -- as a username.
Prevention:
Always use parameterized queries (prepared statements) — the only reliable fix.
Apply least-privilege database users — limit the damage if injection does occur.
Validate and sanitize input as a defense-in-depth layer.
Never expose raw database error messages to users.
Q17: What is the difference between UNION and UNION ALL?
Feature | UNION | UNION ALL |
|---|---|---|
Duplicates | Removes duplicate rows (runs a DISTINCT) | Keeps all rows including duplicates |
Performance | Slower — must sort and deduplicate | Faster — no deduplication step |
When to use | When you need unique results from two queries | When duplicates are acceptable or impossible (different tables) |
Q18: What is a CTE?
A Common Table Expression (CTE) is a named temporary result set defined at the beginning of a query using the WITH keyword. It improves readability and can be referenced multiple times in the same query.
WITH top_customers AS ( SELECT customer_id, SUM(total) AS lifetime_value FROM orders GROUP BY customer_id HAVING SUM(total) > 1000 ) SELECT c.name, tc.lifetime_value FROM customers c JOIN top_customers tc ON tc.customer_id = c.id;
Recursive CTEs can traverse hierarchical data (org charts, category trees) using a self-referencing UNION ALL.
Q19: What are window functions?
Window functions perform calculations across a set of rows related to the current row without collapsing them into groups (unlike GROUP BY). They use the OVER() clause.
SELECT name, salary, AVG(salary) OVER (PARTITION BY department) AS dept_avg, RANK() OVER (PARTITION BY department ORDER BY salary DESC) AS dept_rank, SUM(salary) OVER (ORDER BY hire_date ROWS UNBOUNDED PRECEDING) AS running_total FROM employees;
Common window functions: ROW_NUMBER(), RANK(), DENSE_RANK(), LAG(), LEAD(), FIRST_VALUE(), LAST_VALUE(), SUM(), AVG(), COUNT().
Q20: What is the difference between WHERE and HAVING?
Feature | WHERE | HAVING |
|---|---|---|
Filters | Individual rows before grouping | Groups after GROUP BY |
Can use aggregates | No | Yes — HAVING COUNT(*) > 5 |
Performance | Faster — reduces rows before aggregation | Slower — aggregation happens first |
Q21: What is a covering index?
A covering index is an index that contains all the columns a query needs, so MySQL can answer the query entirely from the index without reading the actual table rows. You will see Using index in the EXPLAIN Extra column.
-- This query is covered by an index on (status, created_at, total) -- MySQL never touches the orders table rows SELECT status, created_at, total FROM orders WHERE status = 'completed' ORDER BY created_at DESC;
Q22: What are the differences between NOW(), SYSDATE(), and CURRENT_TIMESTAMP?
Function | Returns | Evaluated |
|---|---|---|
NOW() | Current datetime at statement start | Once per statement — consistent within a statement |
SYSDATE() | Current datetime at function call time | Each time it is called — may differ within a statement |
CURRENT_TIMESTAMP | Alias for NOW() | Same as NOW() |
Q23: What is the purpose of EXPLAIN FORMAT=JSON?
EXPLAIN FORMAT=JSON produces the query execution plan as a JSON document, providing additional detail not shown in the tabular format including estimated cost values, buffer usage, and a clearer representation of subquery nesting. This is particularly useful for complex queries with multiple subqueries or derived tables.
Q24: How does InnoDB handle concurrency?
InnoDB uses Multi-Version Concurrency Control (MVCC) — readers see a snapshot of the data as it existed at the start of their transaction, without blocking writers.
Writers use row-level locks — only the specific rows being modified are locked, not the entire table.
Gap locks and next-key locks prevent phantom reads at REPEATABLE READ isolation level.
Deadlocks are detected automatically and resolved by rolling back the transaction with the smallest undo log.
Q25: What happens when you run ALTER TABLE on a large table?
In older MySQL versions, most ALTER TABLE operations required rebuilding the entire table while holding a metadata lock — blocking all reads and writes for the duration.
MySQL 5.6+ Online DDL allows many operations to proceed while the table remains readable and writable:
Adding/dropping secondary indexes — online (no table copy).
Adding nullable columns (MySQL 8.0) — instant (no table copy needed).
Changing column data type — still requires a table rebuild.
Adding/dropping primary keys — requires a full table rebuild.
-- Check if the operation will be online or will lock the table ALTER TABLE orders ADD COLUMN notes TEXT, ALGORITHM=INSTANT, -- fastest: only metadata change LOCK=NONE; -- fail if locking would be required