Top SQL Interview Questions for Freshers: Queries, Real-World Data, and Whiteboard Logic

Technical screening rounds in 2026 evaluate whether you understand data behavior, null constraints, and table relationships before writing code. Below are the most critical SQL interview questions, structured with real input datasets, production query solutions, expected outputs, and whiteboard evaluation logic.
1. How do you find the second highest salary when salaries are duplicated?
Business Scenario: HR wants to identify the second highest compensation bracket, but multiple senior executives earn identical top salaries.
Input Table: employees
emp_id | emp_name | department | salary |
101 | Rohit | Engineering | 95,000 |
102 | Ananya | Engineering | 95,000 |
103 | Vikas | Product | 82,000 |
104 | Priya | Marketing | 75,000 |
105 | Aman | Analytics | 82,000 |
SQL Query:
SQL
WITH RankedSalaries AS (
SELECT
emp_name,
salary,
DENSE_RANK() OVER (ORDER BY salary DESC) AS rnk
FROM employees
)
SELECT emp_name, salary
FROM RankedSalaries
WHERE rnk = 2;
Expected Output:
emp_name | salary |
Vikas | 82,000 |
Aman | 82,000 |
The Whiteboard Defense:
If you use ROW_NUMBER(), it arbitrarily assigns ranks 1 and 2 to Rohit and Ananya, returning ₹95,000 as the second highest salary—which is incorrect.
If you use RANK(), ranks become 1, 1, 3 (skipping rank 2 entirely), returning zero rows.
DENSE_RANK() assigns ranks 1, 1, 2, 2, 3, ensuring the true second-highest salary (₹82,000) is returned along with all employees earning it.

To master relational schemas before tackling window functions, explore: Data Engineering with SQL: An Introductory Guide.
2. How do you find customers who signed up but never placed an order?
Business Scenario: The growth marketing team needs a list of registered users who have zero purchase activity for an activation discount campaign.
Input Tables:
customers Table:
customer_id | name | signup_date |
1 | Aarav | 2026-01-10 |
2 | Neha | 2026-01-15 |
3 | Kabir | 2026-02-01 |
4 | Simran | 2026-02-12 |
orders Table:
order_id | customer_id | order_amount |
5001 | 1 | 2,400 |
5002 | 1 | 1,100 |
5003 | 3 | 4,950 |
SQL Query:
SQL
SELECT
c.customer_id,
c.name,
c.signup_date
FROM customers c
LEFT JOIN orders o
ON c.customer_id = o.customer_id
WHERE o.order_id IS NULL;
Expected Output:
customer_id | name | signup_date |
2 | Neha | 2026-01-15 |
4 | Simran | 2026-02-12 |
The Whiteboard Defense:
A LEFT JOIN keeps all records from customers. For customers without matching orders, the columns from orders populate as NULL. Filtering by WHERE o.order_id IS NULL isolates unactivated users without expensive correlated subqueries.
If you are connecting your database outputs to automated Python scripts, check out: Why Python and MySQL Are Perfect for Data Science.
3. How do you calculate Month-over-Month (MoM) revenue growth?
Business Scenario: Finance requires executive reporting on whether monthly revenue is expanding or contracting, alongside the percentage change.
Input Table: monthly_revenue
sales_month | total_revenue |
2026-01-01 | 1,00,000 |
2026-02-01 | 1,30,000 |
2026-03-01 | 1,17,000 |
SQL Query:
SQL
SELECT
sales_month,
total_revenue,
LAG(total_revenue, 1) OVER (ORDER BY sales_month) AS prev_month_revenue,
ROUND(
((total_revenue - LAG(total_revenue, 1) OVER (ORDER BY sales_month)) * 100.0) /
LAG(total_revenue, 1) OVER (ORDER BY sales_month),
2
) AS mom_growth_pct
FROM monthly_revenue;
Expected Output:
sales_month | total_revenue | prev_month_revenue | mom_growth_pct |
2026-01-01 | 1,00,000 | NULL | NULL |
2026-02-01 | 1,30,000 | 1,00,000 | 30.00 |
2026-03-01 | 1,17,000 | 1,30,000 | -10.00 |
The Whiteboard Defense:
The LAG() window function inspects the previous chronological row without requiring a self-join. Multiplying the numerator by 100.0 prevents integer division truncation in database engines like PostgreSQL or SQL Server.
To integrate commercial metrics into visual KPI dashboards, read: What Is Power BI? A Beginner's Guide.
4. How do you remove duplicate rows while keeping only the earliest entry?
Business Scenario: A webhook sync error inserted duplicate lead entries with identical emails into the database.
Input Table: leads
lead_id | lead_source | created_at | |
1 | Google Ads | 2026-03-01 10:00:00 | |
2 | Organic | 2026-03-01 10:15:00 | |
3 | Referral | 2026-03-01 11:30:00 | |
4 | Direct | 2026-03-01 12:00:00 |
SQL Query:
SQL
WITH RankedDuplicates AS (
SELECT
lead_id,
ROW_NUMBER() OVER (
PARTITION BY email
ORDER BY created_at ASC
) AS row_num
FROM leads
)
DELETE FROM leads
WHERE lead_id IN (
SELECT lead_id
FROM RankedDuplicates
WHERE row_num > 1
);
Remaining Records in leads Table:
lead_id | lead_source | created_at | |
1 | Google Ads | 2026-03-01 10:00:00 | |
3 | Referral | 2026-03-01 11:30:00 |
The Whiteboard Defense:
PARTITION BY email groups the records per unique email address, while ORDER BY created_at ASC assigns row_num = 1 to the first chronological submission.
Filtering for row_num > 1 safely targets only the redundant copies for deletion.
For data cleaning tasks conducted inside Python scripts, explore: How to Use Python for Data Analysis: A Beginner's Guide.
5. How do you compute a rolling 3-month average of active users?
Business Scenario: Product teams track smoothed engagement trends to remove weekend and seasonal spikes from daily counts.
Input Table: daily_active_users
activity_date | active_users |
2026-01-01 | 1,200 |
2026-02-01 | 1,500 |
2026-03-01 | 1,800 |
2026-04-01 | 2,100 |
SQL Query:
SQL
SELECT
activity_date,
active_users,
ROUND(
AVG(active_users) OVER (
ORDER BY activity_date
ROWS BETWEEN 2 PRECEDING AND CURRENT ROW
), 1
) AS rolling_3_month_avg
FROM daily_active_users;
Expected Output:
activity_date | active_users | rolling_3_month_avg | Calculation Context |
2026-01-01 | 1,200 | 1200.0 | (1200 / 1) |
2026-02-01 | 1,500 | 1350.0 | (1200 + 1500) / 2 |
2026-03-01 | 1,800 | 1500.0 | (1200 + 1500 + 1800) / 3 |
2026-04-01 | 2,100 | 1800.0 | (1500 + 1800 + 2100) / 3 |
The Whiteboard Defense:
The framing clause ROWS BETWEEN 2 PRECEDING AND CURRENT ROW explicitly restricts the moving window to the current row plus the two preceding rows, preventing unbound default frame errors (RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW).

Master Technical Whiteboard Interviews at IOTA Academy
Memorizing SQL templates on your own leaves you vulnerable when interviewers change column constraints or ask you to debug query joins live on a screen.
At IOTA Academy Indore, IIT alumni and industry data practitioners train freshers inside offline learning labs using uncurated production schemas[cite: 1, 2]:
Data Analytics with Gen AI (6 Months): Master production SQL joins, window functions, Python wrangling, and Power BI dashboards.
Business Analytics with Gen AI (4 Months): Focus on commercial SQL extraction, Excel modeling, and executive KPI reporting.
Data Science with Gen AI (8 Months): Combine advanced SQL pipelines with predictive modeling, machine learning, and generative AI systems.
Weekly Live Whiteboard Drills: Solve relational database problems under interview pressure, explain query logic without a keyboard, and debug execution bottlenecks.
Build verified technical problem-solving skills that pass corporate screenings: Explore IOTA Academy Courses & Book Your Free 1-on-1 Counseling Session Today.
top 10 sql interview questions and query answers for freshers 2026
sql interview queries for freshers
sql technical interview questions for data analyst
sql window functions interview questions
dense_rank vs rank vs row_number
find second highest salary in sql without max
how to delete duplicate rows in sql using cte
sql mom growth percentage query lag function
difference between where and having in sql
sql left join find non matching records
sql training in indore iota academy
data analytics practical coaching indore
sql whiteboard mock interview indore
best institute for sql and data science in indore






Comments