150+ SQL & MySQL Scenario-Based Interview Questions
Learn to solve real database problems—not just memorize syntax. Practise joins, duplicates, subqueries, window functions, transactions, query tuning, and SQL validation scenarios asked in QA, SDET, backend, and data-focused interviews.
Best for: QA Engineers, SDETs, Automation Engineers, Backend Developers, Data Analysts, and Automation Leads.
Jump to a topic
1. Start with a practice schema
Use these sample tables to practise the questions below. Examples use MySQL 8.0+ syntax.
CREATE TABLE departments (
department_id INT PRIMARY KEY,
department_name VARCHAR(100)
);
CREATE TABLE employees (
employee_id INT PRIMARY KEY,
employee_name VARCHAR(100),
department_id INT,
salary DECIMAL(10,2),
manager_id INT,
hire_date DATE,
FOREIGN KEY (department_id)
REFERENCES departments(department_id)
);
CREATE TABLE customers (
customer_id INT PRIMARY KEY,
customer_name VARCHAR(100),
email VARCHAR(150),
city VARCHAR(100)
);
CREATE TABLE orders (
order_id INT PRIMARY KEY,
customer_id INT,
order_date DATETIME,
amount DECIMAL(10,2),
status VARCHAR(30),
FOREIGN KEY (customer_id)
REFERENCES customers(customer_id)
);
CREATE TABLE payments (
payment_id INT PRIMARY KEY,
order_id INT,
payment_amount DECIMAL(10,2),
payment_status VARCHAR(30),
payment_date DATETIME
);
2. SQL fundamentals: questions 1–20
| # | Scenario | Concept |
|---|---|---|
| 1 | Find employees earning more than ₹50,000. | WHERE |
| 2 | Find salaries between ₹40,000 and ₹80,000. | BETWEEN |
| 3 | Find employees whose names start with A. | LIKE |
| 4 | Find employees without a department. | NULL / LEFT JOIN |
| 5 | Find employees whose salary is missing. | IS NULL |
| 6 | List unique customer cities. | DISTINCT |
| 7 | Count all employees. | COUNT |
| 8 | Calculate average salary. | AVG |
| 9 | Find minimum and maximum salary. | MIN / MAX |
| 10 | Calculate total salary expense. | SUM |
| 11 | Count employees per department. | GROUP BY |
| 12 | Find departments with more than five employees. | HAVING |
| 13 | Find customers from Indore or Bhopal. | IN |
| 14 | Find employees hired during 2025. | Date ranges |
| 15 | Sort employees by salary descending. | ORDER BY |
| 16 | Return the five highest-paid employees. | LIMIT |
| 17 | Display missing salary as zero. | COALESCE |
| 18 | Display employee names in uppercase. | UPPER |
| 19 | Extract the year from a hire date. | YEAR |
| 20 | Find employees earning above company average. | Subquery |
Example: Employees earning above the company average
SELECT employee_name, salary
FROM employees
WHERE salary > (
SELECT AVG(salary) FROM employees
);
Example: Find employees hired during 2025
SELECT employee_id, employee_name, hire_date
FROM employees
WHERE hire_date >= '2025-01-01'
AND hire_date < '2026-01-01';
Using a range can allow better use of an index on the date column than wrapping the column in YEAR().
3. JOIN scenarios: questions 21–35
| # | Scenario | Approach |
|---|---|---|
| 21 | Display every order with its customer name. | INNER JOIN |
| 22 | Find customers who never ordered. | LEFT JOIN + IS NULL |
| 23 | Find orders whose customer is missing. | LEFT JOIN from orders |
| 24 | Show all customers, including those without orders. | LEFT JOIN |
| 25 | Display employee names and department names. | JOIN |
| 26 | Find employees without a valid department. | LEFT JOIN |
| 27 | Display each employee and their manager. | Self join |
| 28 | Find customers with more than three orders. | JOIN + GROUP BY |
| 29 | Display orders with payment details. | Multi-table join |
| 30 | Find orders with no payment record. | NOT EXISTS |
| 31 | Find customers with successful payments. | JOIN + filter |
| 32 | Show departments with zero employees too. | LEFT JOIN |
| 33 | Find employees in the same department as a given employee. | Self join |
| 34 | Find customers who ordered and paid. | Multi-table joins |
| 35 | Find customers with orders but no completed payment. | NOT EXISTS |
Example: Customers who never placed an order
SELECT c.customer_id, c.customer_name
FROM customers c
LEFT JOIN orders o
ON c.customer_id = o.customer_id
WHERE o.order_id IS NULL;
Example: Employee and manager names
SELECT e.employee_name AS employee,
m.employee_name AS manager
FROM employees e
LEFT JOIN employees m
ON e.manager_id = m.employee_id;
Example: Orders with no payment record
SELECT o.order_id
FROM orders o
WHERE NOT EXISTS (
SELECT 1
FROM payments p
WHERE p.order_id = o.order_id
);
4. Duplicate records & data-quality scenarios: questions 36–55
Example: Find duplicate email addresses
SELECT email, COUNT(*) AS occurrences
FROM customers
WHERE email IS NOT NULL
GROUP BY email
HAVING COUNT(*) > 1;
Example: Return all customer rows with duplicate emails
SELECT c.*
FROM customers c
JOIN (
SELECT email
FROM customers
WHERE email IS NOT NULL
GROUP BY email
HAVING COUNT(*) > 1
) d ON d.email = c.email;
Example: Find missing or blank emails
SELECT *
FROM customers
WHERE email IS NULL OR TRIM(email) = '';
Example: Find negative order amounts
SELECT *
FROM orders
WHERE amount < 0;
Example: Find payment amounts that differ from order amounts
This example assumes one successful payment should equal the order amount. If split or partial payments are allowed, aggregate successful payments first.
SELECT o.order_id,
o.amount AS order_amount,
p.payment_amount
FROM orders o
JOIN payments p ON p.order_id = o.order_id
WHERE p.payment_status = 'SUCCESS'
AND p.payment_amount <> o.amount;
More practice questions
- Find duplicate rows based on customer name and city.
- Find duplicate orders for a customer on the same date—after confirming the business rule.
- Find orphan payment records.
- Find names with leading or trailing spaces.
- Find duplicate emails after normalizing case and whitespace.
- Find missing IDs in a sequence.
- Find orders with future dates.
- Identify malformed email addresses.
- Find records where created date is later than updated date.
- Validate mandatory columns across several tables.
- Compare source and target row counts.
- Identify repeated transactions caused by retries.
- Find values outside an approved status list.
- Detect inconsistent whitespace or capitalization in category fields.
- Find duplicates without a unique identifier.
- Design a safe deduplication process that retains the correct record.
- Find rows that violate a business-specific data rule.
- Identify unexpected NULL values in required fields.
5. Advanced SQL & window functions: questions 56–75
Second-highest distinct salary
SELECT MAX(salary) AS second_highest_salary
FROM employees
WHERE salary < (SELECT MAX(salary) FROM employees);
Top three distinct salary levels in each department
WITH ranked AS (
SELECT employee_id, employee_name, department_id, salary,
DENSE_RANK() OVER (
PARTITION BY department_id
ORDER BY salary DESC
) AS salary_rank
FROM employees
WHERE salary IS NOT NULL
)
SELECT *
FROM ranked
WHERE salary_rank <= 3;
Latest order for each customer
WITH ranked AS (
SELECT o.*,
ROW_NUMBER() OVER (
PARTITION BY customer_id
ORDER BY order_date DESC, order_id DESC
) AS rn
FROM orders o
)
SELECT *
FROM ranked
WHERE rn = 1;
Running total of orders
SELECT order_id, order_date, amount,
SUM(amount) OVER (
ORDER BY order_date, order_id
ROWS BETWEEN UNBOUNDED PRECEDING
AND CURRENT ROW
) AS running_total
FROM orders;
Compare each order with the customer’s previous order
SELECT customer_id, order_id, order_date, amount,
LAG(amount) OVER (
PARTITION BY customer_id
ORDER BY order_date, order_id
) AS previous_amount
FROM orders;
Other advanced challenges
- Find employees whose salary equals their department’s maximum.
- Find the median salary.
- Calculate month-over-month sales percentage change.
- Find customers who ordered in every month of a quarter.
- Find products contributing to the first 80% of cumulative sales.
- Find employees earning more than every employee in another department.
- Find customers who ordered in January but not February.
6. MySQL internals & performance: questions 76–90
| # | Scenario | What to discuss |
|---|---|---|
| 76 | A query is slow despite an index. | EXPLAIN, selectivity, statistics, access path |
| 77 | A join unexpectedly returns duplicates. | Join cardinality and one-to-many relationships |
| 78 | An indexed date query scans too many rows. | Sargability and date ranges |
| 79 | Two users update the same balance. | Transactions and row locks |
| 80 | A multi-table update fails partway through. | ROLLBACK and atomicity |
| 81 | Two transactions wait on each other. | Deadlocks and retry strategy |
| 82 | Compare DELETE, TRUNCATE and DROP. | Rows vs table; implicit commit |
| 83 | A comparison involving NULL gives unexpected results. | Three-valued logic |
| 84 | Prevent duplicate email addresses. | UNIQUE constraint |
| 85 | Return the top ten rows after filtering. | Clause semantics and ordering |
| 86 | Choose a composite index. | Column order and workload |
| 87 | NOT IN unexpectedly returns no rows. | NULL in subquery |
| 88 | Investigate a deadlock. | InnoDB status and Performance Schema |
| 89 | Prevent a lost update. | Locking or optimistic concurrency |
| 90 | LIMIT returns different rows. | Deterministic ORDER BY |
Use EXPLAIN to inspect a query plan
EXPLAIN
SELECT *
FROM orders
WHERE customer_id = 1001;
Review the access type, selected key, estimated rows, join order, and extra operations. On supported MySQL versions, EXPLAIN ANALYZE executes the query and reports actual timing and row counts; use it cautiously for expensive queries.
DELETE vs TRUNCATE vs DROP
- DELETE: removes selected rows and supports WHERE. Transactional rollback depends on the storage engine and transaction context.
- TRUNCATE TABLE: removes all rows; in MySQL it is DDL and causes an implicit commit.
- DROP TABLE: removes the table definition and its data.
Why can NOT IN fail with NULL?
SELECT customer_id
FROM customers
WHERE customer_id NOT IN (
SELECT customer_id FROM orders
);
If the subquery contains NULL, comparisons can evaluate to UNKNOWN and exclude rows unexpectedly. NOT EXISTS is often a safer alternative.
7. SQL for QA & SDET interviews: questions 91–120
| # | Scenario | Validation focus |
|---|---|---|
| 91 | API returns 100 customers but database has 105. | Pagination, filters, authorization, timing |
| 92 | Validate newly registered user data. | Field-level assertions and unique test data |
| 93 | Order API succeeds but no row is visible. | Commit, async processing, environment, logs |
| 94 | Ensure an order is not charged twice. | Idempotency and payment business rules |
| 95 | Reconcile order total with line items. | Quantity, price, tax, discounts, rounding |
| 96 | Verify database state after an update API. | Before/after state and unchanged fields |
| 97 | Validate a delete API. | Soft delete vs physical delete |
| 98 | Test rollback after a multi-step failure. | Atomicity and related tables |
| 99 | Compare source and target after migration. | Counts, keys, fields, aggregates, hashes |
| 100 | Find source rows missing from target. | Anti-join / staged key comparison |
| 101 | Registration passes but login fails. | Stored state, activation, credentials flow |
| 102 | API status is correct but stored value is wrong. | Database assertion against request |
| 103 | Order completed but payment failed. | State consistency and workflow rules |
| 104 | Validate refund across order, payment and balance. | Reconciliation and transactions |
| 105 | Retry creates duplicate records. | Idempotency keys and uniqueness |
| 106 | Validate audit timestamps. | Time zone, precision, update rules |
| 107 | Concurrent requests create the same customer. | Unique constraints and race conditions |
| 108 | Validate API pagination and sorting. | Stable ordering and expected IDs |
| 109 | Prevent access to another customer’s data. | Authorization and tenant boundaries |
| 110 | Validate a batch job processes eligible rows once. | Eligibility, retries, idempotency |
| 111 | Test foreign-key behavior. | Parent/child integrity |
| 112 | Test a stored procedure. | Valid, invalid, boundary inputs |
| 113 | Test a trigger that writes audit records. | Side effects and transaction behavior |
| 114 | Validate a scheduled job’s date range. | Boundaries, time zones, cutoff times |
| 115 | Test dirty, non-repeatable and phantom reads. | Isolation levels |
| 116 | Connection drops during a transaction. | Rollback, retry, uncertain commit |
| 117 | Test deadlock recovery. | Retry handling and consistency |
| 118 | Validate schema migration. | Backward compatibility and data integrity |
| 119 | Several users buy the last inventory item. | Conditional update and locking |
| 120 | Design reusable SQL assertions for automation. | Connection management and test isolation |
Example: Find orders without a successful payment
SELECT o.order_id
FROM orders o
WHERE NOT EXISTS (
SELECT 1
FROM payments p
WHERE p.order_id = o.order_id
AND p.payment_status = 'SUCCESS'
);
Example: Safe inventory decrement
UPDATE inventory
SET stock_quantity = stock_quantity - 1
WHERE product_id = 101
AND stock_quantity > 0;
Check the affected-row count. A complete checkout should use a transaction for the inventory change and related order records. Also account for the case where the product ID does not exist.
8. Transactions, constraints & schema design: questions 121–135
| # | Scenario | Topic |
|---|---|---|
| 121 | Prevent duplicate registration emails. | UNIQUE |
| 122 | Prevent orders referencing missing customers. | Foreign key |
| 123 | Prevent a negative account balance. | Constraint and transaction logic |
| 124 | Undo updates after a failure. | ROLLBACK |
| 125 | Undo part of a transaction. | SAVEPOINT |
| 126 | Explain the role of COMMIT. | Transaction lifecycle |
| 127 | Choose isolation for a banking workflow. | Isolation levels |
| 128 | Prevent selling more stock than available. | Conditional update and locks |
| 129 | Compare primary and unique keys. | Constraints |
| 130 | Choose CHAR, VARCHAR, TEXT or numeric types. | Data modeling |
| 131 | Decide whether to normalize tables. | Normalization |
| 132 | Optimize a reporting workload. | Denormalization trade-offs |
| 133 | Add indexes without harming writes unnecessarily. | Index trade-offs |
| 134 | Record changes to customer records. | Audit/history design |
| 135 | Change schema while application is running. | Safe migrations |
Example: transaction pattern
START TRANSACTION;
UPDATE accounts
SET balance = balance - 500
WHERE account_id = 101
AND balance >= 500;
-- Application must check affected rows.
-- Apply related updates only when valid.
COMMIT;
-- On a handled failure, use ROLLBACK instead.
For a transfer, debit and credit should happen in one transaction. Use InnoDB, validate affected-row counts, and design locking and retry behavior deliberately.
9. Tricky SQL output questions: 136–150
- Why does COUNT(*) differ from COUNT(email)?
- How do WHERE and HAVING differ?
- What happens when a scalar subquery returns multiple rows?
- Why can joining two one-to-many tables inflate totals?
- How do UNION and UNION ALL differ?
- When is EXISTS preferable to a JOIN?
- Why is salary = NULL incorrect?
- Why can GROUP BY queries fail with unrelated selected columns?
- How do ROW_NUMBER, RANK and DENSE_RANK differ?
- Why can a date filter miss records on the final day?
- Find the third occurrence of an event for each user.
- Find gaps in a sequence of dates.
- Why can OR conditions affect query performance?
- How can implicit type conversion affect indexes?
- Return the latest row per group when timestamps can tie.
COUNT(*) vs COUNT(email)
SELECT COUNT(*) AS total_rows,
COUNT(email) AS rows_with_email
FROM customers;
COUNT(*) counts all rows. COUNT(email) excludes rows where email is NULL.
UNION vs UNION ALL
SELECT city FROM customers
UNION
SELECT city FROM suppliers;
UNION removes duplicate result rows; UNION ALL preserves them and is generally cheaper when duplicate removal is unnecessary.
10. Interactive practice tracker
Check off each challenge after solving it without help. Your progress is tracked in this page while it remains open.
11. Quick knowledge-check quiz
Choose an answer for each question, then select Check my answers to see your score and explanations.
12. Seven-day SQL interview study plan
SQL fundamentals
WHERE, aggregates, NULL, GROUP BY, HAVING.
Joins & subqueries
INNER/LEFT JOIN, anti-joins, EXISTS, correlated subqueries.
Advanced SQL
CTEs, ranking, running totals, LAG and LEAD.
MySQL fundamentals
Indexes, constraints, data types, normalization.
Transactions & performance
ACID, locks, deadlocks, EXPLAIN, isolation.
QA database testing
API-to-DB checks, migration, reconciliation, concurrency.
Mock interview
Solve 15–20 queries without assistance and explain trade-offs.
13. How to answer SQL interview questions confidently
- Clarify the rule: What does the business mean by “duplicate”, “latest”, or “successful”?
- Design the query: Identify tables, joins, filters, grouping and ordering.
- Cover edge cases: Check NULLs, ties, empty results, duplicates and date boundaries.
- Consider performance: Discuss indexes and validate the execution plan with EXPLAIN.
- Connect it to automation: Explain how your test reads data and asserts the result.
- Protect data: Use isolated test data, least-privilege access and safe cleanup.
Compatibility note: The interactive tracker and quiz use JavaScript. Some WordPress editors or security plugins strip inline scripts from post content. If that happens, keep the article markup in the post and add the script through an approved snippets plugin or your theme’s enqueued JavaScript. Avoid pasting untrusted scripts into production.