SQL Interview Mastery
20 Must-Know SQL Questions — From queries to concepts. Write clean SQL, explain clearly, nail the interview.
Find Nth Highest Salary
Query Most Asked 3 minThis is THE most asked SQL interview question. There are multiple approaches, and interviewers want to see you understand the trade-offs. The trick is handling duplicate salaries — if 3 people have the highest salary, what's the "2nd highest"?
Key Insight
- DENSE_RANK vs RANK vs ROW_NUMBER: DENSE_RANK handles ties without gaps (100, 100, 90 → ranks 1, 1, 2). RANK creates gaps (1, 1, 3). ROW_NUMBER gives unique numbers (1, 2, 3).
- Use DISTINCT with LIMIT/OFFSET approach to handle duplicate salaries
- DENSE_RANK is the most interview-friendly — shows you know window functions
Common Mistake
Using RANK() instead of DENSE_RANK(). If two people share the highest salary, RANK() makes the next salary rank 3, not 2. Also, forgetting DISTINCT with LIMIT/OFFSET returns wrong results when salaries repeat.
Find 2nd Highest Salary
Query 2 minKey Insight
The subquery wrapper
SELECT (SELECT ...) AS alias ensures NULL is
returned if no result exists. Without it, you'd get an empty
result set — which might not be what the interviewer wants.
LeetCode specifically tests this edge case!
Find Duplicate Records
Query 2 minKey Insight
The first query shows WHICH values are duplicated and HOW MANY times. The third query shows ALL the actual rows that are duplicates. Know both — interviewers may ask for either.
Delete Duplicate Rows
Query Tricky 3 minCommon Mistake
In MySQL, you can't directly reference the same table in a subquery of a DELETE. That's why Method 3 wraps the subquery in another SELECT — it's a workaround. Also, always test DELETE queries with SELECT first!
Key Insight
ROW_NUMBER approach is most versatile — works for complex duplicates (multiple columns), lets you control which duplicate to keep (first inserted, latest, etc.). Self-join is faster but only works for simple cases.
Highest Salary by Department
Query 2 minKey Insight
The window function approach handles ties naturally — if two
people share the max salary, both are returned with rank 1. The
subquery approach with tuple comparison
(dept, salary) IN (...) also handles this. Simple
MAX() only gives you the value, not who has it.
Employees Above Department Average
Query 2 minKey Insight
Window function is more efficient — it calculates the average once per partition and adds it to every row. Correlated subquery recalculates for each row (O(n²) vs O(n)). Show the window function approach to impress.
Employees with Same Salary
Query 2 minEmployee & Manager (Self Join)
Query Classic 3 minSample Employee Table
| id | name | manager_id |
|---|---|---|
| 1 | John (CEO) | NULL |
| 2 | Jane | 1 |
| 3 | Bob | 1 |
| 4 | Alice | 2 |
Key Insight
- Use LEFT JOIN to include employees without managers (like CEO)
- Use INNER JOIN if you only want employees WHO HAVE managers
- The same table is used twice with different aliases (e for employee, m for manager)
Follow-up Questions
Find employees who earn more than their manager?
SELECT e.name AS employee, e.salary AS emp_salary,
m.name AS manager, m.salary AS mgr_salary
FROM Employee e
INNER JOIN Employee m ON e.manager_id = m.id
WHERE e.salary > m.salary;
Departments with 5+ Employees
Query 2 minCustomers Who Never Ordered
Query 2 minCommon Mistake
NOT IN with NULLs: If the subquery returns any
NULL values, NOT IN returns no rows! Always add
WHERE column IS NOT NULL inside the subquery, or
use NOT EXISTS which handles NULLs correctly.
Key Insight
LEFT JOIN + IS NULL is typically fastest and most readable. NOT EXISTS is also efficient and NULL-safe. NOT IN is trickiest due to NULL behavior. Know all three — interviewers may ask why you chose one over another.
DELETE vs DROP vs TRUNCATE
Difference Most Asked 3 min| Aspect | DELETE | TRUNCATE | DROP |
|---|---|---|---|
| What it removes | Specific rows (or all) | All rows only | Entire table + structure |
| WHERE clause | ✓ Yes | ✗ No | ✗ No |
| Rollback | ✓ Yes (logged) | ✗ No (most DBs) | ✗ No |
| Speed | Slow (row by row) | Fast (deallocate pages) | Fast |
| Identity/Auto-increment | Keeps counter | Resets to seed | N/A |
| Triggers fire | ✓ Yes | ✗ No | ✗ No |
| Type | DML | DDL | DDL |
Key Insight
- DELETE is like erasing text — slow but recoverable
- TRUNCATE is like getting a new blank sheet — fast, keeps the paper
- DROP is like throwing away the entire notebook
- TRUNCATE resets auto-increment; DELETE doesn't
Competitive Edge
Mention that TRUNCATE is DDL (Data Definition Language) despite looking like DML. This means it causes an implicit COMMIT in most databases. Also, TRUNCATE requires ALTER permission on the table, not just DELETE permission. These details show deep understanding.
WHERE vs HAVING
Difference 2 min| Aspect | WHERE | HAVING |
|---|---|---|
| When applied | Before GROUP BY | After GROUP BY |
| Aggregate functions | ✗ Cannot use | ✓ Can use |
| Works without GROUP BY | ✓ Yes | Technically yes, but rarely useful |
| Purpose | Filter individual rows | Filter grouped results |
Key Insight
SQL Execution Order: FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY. This is why WHERE can't use aliases defined in SELECT, and why HAVING can use aggregate functions (it runs after GROUP BY).
INNER JOIN vs LEFT JOIN
Difference Visual 3 min| Join Type | Returns | NULLs |
|---|---|---|
INNER JOIN |
Only matching rows from both | No NULLs from join |
LEFT JOIN |
All from left + matching from right | NULLs for non-matching right |
RIGHT JOIN |
All from right + matching from left | NULLs for non-matching left |
FULL OUTER JOIN |
All from both tables | NULLs on both sides where no match |
CROSS JOIN |
Cartesian product (m × n rows) | No condition, every combination |
Key Insight
- LEFT JOIN is used to find "missing" records (customers with no orders)
- INNER JOIN is default when you just write JOIN
- You can convert any RIGHT JOIN to LEFT JOIN by swapping table order
- FULL OUTER JOIN is not supported in MySQL — use UNION of LEFT and RIGHT
Normalization & Normal Forms
Concept Theory 4 minWhy normalize? Without normalization, you get: (1) Update anomalies — change one place, forget another. (2) Insert anomalies — can't add data without unrelated data. (3) Delete anomalies — deleting one thing loses other info.
| Normal Form | Rule | Simple Explanation |
|---|---|---|
| 1NF | Atomic values, no repeating groups | Each cell has ONE value. No arrays/lists in a column. |
| 2NF | 1NF + No partial dependencies | Every non-key column depends on the WHOLE primary key (matters for composite keys). |
| 3NF | 2NF + No transitive dependencies | Non-key columns don't depend on other non-key columns. A→B→C is bad. |
| BCNF | For every X→Y, X is a superkey | Stricter 3NF. Every determinant must be a candidate key. |
Quick Memory Trick
"The key, the whole key, and nothing but the key, so help me Codd."
- 1NF: "The key" — must have a key, atomic values
- 2NF: "The whole key" — depend on ALL of composite key
- 3NF: "Nothing but the key" — no transitive deps through non-keys
Competitive Edge
Know when to denormalize: in read-heavy systems (analytics, reporting), joins are expensive. Duplicating data trades storage for speed. Data warehouses often use denormalized star schemas. Real-world systems balance normalization (integrity) with denormalization (performance).
PRIMARY KEY vs FOREIGN KEY
Difference 2 min| Aspect | PRIMARY KEY | FOREIGN KEY |
|---|---|---|
| Purpose | Uniquely identify rows | Create relationship between tables |
| Uniqueness | Must be unique | Can have duplicates |
| NULL values | Not allowed | Allowed (optional relationship) |
| Count per table | Only 1 | Multiple allowed |
| Auto-creates index | Yes (clustered in most DBs) | No (but recommended) |
Key Insight
Foreign keys enforce referential integrity: you can't insert an employee with a dept_id that doesn't exist in Department. You can't delete a department that has employees (or use ON DELETE CASCADE to auto-delete them).
Indexing & Why It's Used
Concept Performance 3 min
Without index: Database does full table scan —
reads EVERY row to find matches. O(n).
With index: Database uses the index structure
to jump directly to relevant rows. O(log n).
Key Insight
- Index speeds up: SELECT, WHERE, JOIN, ORDER BY on indexed columns
- Index slows down: INSERT, UPDATE, DELETE (must update index too)
- When to index: Frequently queried columns, JOIN columns, WHERE conditions
- When NOT to: Small tables, frequently updated columns, low-cardinality columns
Common Mistake
"More indexes = better" is wrong. Each index consumes storage
and slows down writes. Also, composite index order matters:
INDEX(A, B) helps queries on A or (A and B), but
NOT queries on just B. Leftmost prefix rule!
UNION vs UNION ALL
Difference 2 min| Aspect | UNION | UNION ALL |
|---|---|---|
| Duplicates | Removes duplicates | Keeps all rows |
| Performance | Slower (must sort/compare) | Faster (just appends) |
| Use when | You need distinct results | Duplicates are OK or impossible |
Key Insight
Both UNION types require: same number of columns, compatible data types in corresponding columns. Column names come from the FIRST query. ORDER BY applies to the final combined result (put at the end).
ACID Properties
Concept Core Theory 3 min| Property | Meaning | Example |
|---|---|---|
| Atomicity | Transaction is all-or-nothing | Bank transfer: debit AND credit both happen, or neither does |
| Consistency | DB moves from one valid state to another | Account balance can't go negative if rules forbid it |
| Isolation | Concurrent transactions don't affect each other | Two transfers happening simultaneously get correct results |
| Durability | Committed data survives crashes | After "transfer successful," data persists even if power fails |
Key Insight
- Atomicity: Implemented via transaction logs (undo logs)
- Consistency: Enforced by constraints, triggers, application logic
- Isolation: Implemented via locks or MVCC (multi-version concurrency control)
- Durability: Implemented via write-ahead logging (WAL)
Competitive Edge
Know isolation levels: Read Uncommitted (dirty reads possible) → Read Committed → Repeatable Read → Serializable (strongest). Higher isolation = fewer anomalies but worse performance. MySQL InnoDB default is Repeatable Read. PostgreSQL default is Read Committed.
Clustered vs Non-Clustered Index
Difference 3 min| Aspect | Clustered Index | Non-Clustered Index |
|---|---|---|
| Physical order | Data rows sorted by index | Separate structure, data unsorted |
| Count per table | Only 1 | Multiple (up to 999 in SQL Server) |
| Speed for range queries | Faster (data is contiguous) | Slower (must follow pointers) |
| Storage | No extra storage | Extra storage for index structure |
| Default on | PRIMARY KEY | Other indexes |
Key Insight
Think of clustered index like a phone book sorted by last name — the data IS sorted. Non-clustered is like a book's index at the back — it tells you page numbers, but you still need to flip to those pages. In MySQL InnoDB, the primary key is always the clustered index. In PostgreSQL, there's no automatic clustered index.
Competitive Edge
Non-clustered indexes in InnoDB store the primary key value (not row pointer). So if your primary key is large (like UUID), ALL your indexes become larger. This is why auto-increment INT is often preferred for primary keys — smaller indexes, better performance.
Subquery vs Correlated Subquery
Difference Performance 3 min| Aspect | Regular Subquery | Correlated Subquery |
|---|---|---|
| References outer query | ✗ No | ✓ Yes |
| Execution | Once | Once per outer row |
| Performance | Usually better | Can be slow (O(n²)) |
| Rewrite as | JOIN (sometimes) | Window function (often better) |
Key Insight
- Correlated subquery can often be rewritten as JOIN or window function — usually faster
- EXISTS with correlated subquery is an exception — can be very efficient (stops at first match)
- If you see table alias from outer query used in subquery, it's correlated
Ready to test SQL?
Practice joins, windows, indexes, transactions, and query reasoning.