SQL is the one skill every data analyst interview tests. Some companies ask theory questions, most give you a live problem, and almost all of them ask at least one window-function question.
This list covers what analyst interviews in India commonly ask, grouped from basic to advanced. Every query uses standard SQL and runs on PostgreSQL. Most also work on MySQL 8 and SQL Server with small changes.
We use two simple tables throughout:
-- customers(customer_id, name, city, signup_date)
-- orders(order_id, customer_id, order_date, amount, status)Basic SQL questions
1. What is the difference between WHERE and HAVING?
WHERE filters rows before grouping. HAVING filters groups after aggregation.
SELECT city, COUNT(*) AS customers
FROM customers
WHERE signup_date >= '2026-01-01' -- filters rows
GROUP BY city
HAVING COUNT(*) > 100; -- filters groups2. What is the order in which SQL clauses are executed?
Logically: FROM → JOIN → WHERE → GROUP BY → HAVING → SELECT → DISTINCT → ORDER BY → LIMIT. This is why you cannot use a column alias defined in SELECT inside WHERE, but you can use it in ORDER BY.
3. What is the difference between COUNT(*), COUNT(column) and COUNT(DISTINCT column)?
COUNT(*)counts all rows.COUNT(column)counts rows where that column is not NULL.COUNT(DISTINCT column)counts unique non-NULL values.
4. How do you handle NULL values?
NULL means "unknown", so NULL = NULL is not true. Use IS NULL / IS NOT NULL to test, and COALESCE to replace:
SELECT order_id, COALESCE(status, 'unknown') AS status
FROM orders
WHERE amount IS NOT NULL;5. What is the difference between UNION and UNION ALL?
UNION removes duplicate rows (and is slower because it must check). UNION ALL keeps every row. Use UNION ALL unless you specifically need de-duplication.
6. What is the difference between DELETE, TRUNCATE and DROP?
DELETEremoves selected rows and can useWHERE.TRUNCATEremoves all rows quickly and keeps the table.DROPremoves the table itself.
Analysts rarely run these on production data, but interviewers like to check you know the difference.
Joins
7. Explain the types of joins.
INNER JOIN: only rows that match in both tablesLEFT JOIN: all rows from the left table, matching rows from the right (NULL where none)RIGHT JOIN: the mirror ofLEFT JOINFULL OUTER JOIN: all rows from both sidesCROSS JOIN: every combination of rows
8. Find customers who have never placed an order.
The classic LEFT JOIN ... IS NULL pattern:
SELECT c.customer_id, c.name
FROM customers c
LEFT JOIN orders o ON o.customer_id = c.customer_id
WHERE o.order_id IS NULL;NOT EXISTS is an equally good answer and often clearer:
SELECT c.customer_id, c.name
FROM customers c
WHERE NOT EXISTS (
SELECT 1 FROM orders o WHERE o.customer_id = c.customer_id
);9. Why did my row count go up after a join?
Because the join key is not unique on one side. If a customer has five orders, joining customers to orders gives five rows for that customer. Always check the grain (what one row represents) of each table before joining. This question is a favourite because it catches real-world bugs.
10. What is a self join? Give an example.
A table joined to itself. Example: find pairs of customers from the same city.
SELECT a.name AS customer_a, b.name AS customer_b, a.city
FROM customers a
JOIN customers b
ON a.city = b.city AND a.customer_id < b.customer_id;The < avoids pairing a customer with themselves and listing each pair twice.
Aggregation and GROUP BY
11. Find total revenue and order count per city.
SELECT c.city,
COUNT(o.order_id) AS orders,
SUM(o.amount) AS revenue
FROM customers c
JOIN orders o ON o.customer_id = c.customer_id
WHERE o.status = 'delivered'
GROUP BY c.city
ORDER BY revenue DESC;12. Find the month-wise revenue for 2026.
SELECT DATE_TRUNC('month', order_date) AS month,
SUM(amount) AS revenue
FROM orders
WHERE order_date >= '2026-01-01' AND order_date < '2027-01-01'
GROUP BY 1
ORDER BY 1;Note the date filter uses >= and < rather than BETWEEN. That avoids missing orders on the last day when order_date includes a time.
13. Find duplicate records.
SELECT customer_id, order_date, amount, COUNT(*) AS copies
FROM orders
GROUP BY customer_id, order_date, amount
HAVING COUNT(*) > 1;14. How do you calculate a percentage of total?
SELECT city,
SUM(amount) AS revenue,
ROUND(100.0 * SUM(amount) / SUM(SUM(amount)) OVER (), 2) AS pct_of_total
FROM orders o
JOIN customers c USING (customer_id)
GROUP BY city;SUM(SUM(amount)) OVER () is a window function over the grouped result: the grand total. The 100.0 avoids integer division.
Window functions (the most-asked advanced topic)
15. What is the difference between ROW_NUMBER, RANK and DENSE_RANK?
For scores 90, 90, 80:
| Function | Result |
|---|---|
ROW_NUMBER() | 1, 2, 3 |
RANK() | 1, 1, 3 |
DENSE_RANK() | 1, 1, 2 |
16. Find the second-highest order amount.
SELECT DISTINCT amount
FROM (
SELECT amount, DENSE_RANK() OVER (ORDER BY amount DESC) AS rnk
FROM orders
) t
WHERE rnk = 2;DENSE_RANK handles ties correctly. Interviewers often follow up with "now the Nth highest", which only changes the 2.
17. Find each customer's latest order.
SELECT *
FROM (
SELECT o.*,
ROW_NUMBER() OVER (PARTITION BY customer_id ORDER BY order_date DESC) AS rn
FROM orders o
) t
WHERE rn = 1;18. Calculate a running total of revenue by day.
SELECT order_date,
SUM(amount) AS daily_revenue,
SUM(SUM(amount)) OVER (ORDER BY order_date) AS running_revenue
FROM orders
GROUP BY order_date
ORDER BY order_date;19. Calculate month-over-month growth.
WITH monthly AS (
SELECT DATE_TRUNC('month', order_date) AS month, SUM(amount) AS revenue
FROM orders
GROUP BY 1
)
SELECT month,
revenue,
LAG(revenue) OVER (ORDER BY month) AS prev_revenue,
ROUND(100.0 * (revenue - LAG(revenue) OVER (ORDER BY month))
/ NULLIF(LAG(revenue) OVER (ORDER BY month), 0), 2) AS growth_pct
FROM monthly
ORDER BY month;NULLIF(..., 0) prevents a divide-by-zero error.
20. Calculate a 7-day moving average.
SELECT order_date,
daily_revenue,
AVG(daily_revenue) OVER (
ORDER BY order_date
ROWS BETWEEN 6 PRECEDING AND CURRENT ROW
) AS moving_avg_7d
FROM (
SELECT order_date, SUM(amount) AS daily_revenue
FROM orders
GROUP BY order_date
) d;CTEs and subqueries
21. What is a CTE and why use it?
A Common Table Expression (WITH name AS (...)) is a named, temporary result you can use in the rest of the query. It makes long queries readable, step by step, and interviewers notice clean structure.
22. Find customers whose total spend is above the average customer's spend.
WITH spend AS (
SELECT customer_id, SUM(amount) AS total_spend
FROM orders
GROUP BY customer_id
)
SELECT customer_id, total_spend
FROM spend
WHERE total_spend > (SELECT AVG(total_spend) FROM spend);Case-study questions
These test whether you can turn a business question into SQL.
23. What percentage of customers who signed up in January ordered within 30 days?
WITH jan AS (
SELECT customer_id, signup_date
FROM customers
WHERE signup_date >= '2026-01-01' AND signup_date < '2026-02-01'
)
SELECT ROUND(100.0 * COUNT(DISTINCT o.customer_id) / COUNT(DISTINCT j.customer_id), 2)
AS pct_ordered_within_30d
FROM jan j
LEFT JOIN orders o
ON o.customer_id = j.customer_id
AND o.order_date < j.signup_date + INTERVAL '30 days';The 30-day condition sits in the ON clause, not WHERE, so customers without an order are still counted in the denominator.
24. Find customers who ordered in three consecutive months.
WITH months AS (
SELECT DISTINCT customer_id, DATE_TRUNC('month', order_date) AS month
FROM orders
),
flagged AS (
SELECT customer_id, month,
LAG(month, 2) OVER (PARTITION BY customer_id ORDER BY month) AS two_back
FROM months
)
SELECT DISTINCT customer_id
FROM flagged
WHERE two_back = month - INTERVAL '2 months';25. "Revenue dropped 15% last week. How would you investigate with SQL?"
There is no single query. Interviewers want your process:
- Confirm the drop is real: check for missing data or a broken pipeline first.
- Split by dimension: city, product category, channel, new vs returning customers. Find where the drop is concentrated.
- Split the metric: revenue = orders × average order value. Did orders fall, or order size?
- Check events: price changes, stock-outs, app releases, holidays.
- Recommend a next step, with the numbers that support it.
Talking through this calmly is often worth more than any single query.
How to practise
- Load a free sample dataset into PostgreSQL and write every query above from memory.
- Time yourself: most live rounds give 10 to 15 minutes per question.
- Explain your query out loud as you write it. Interviewers grade reasoning, not just the result.
- Use an AI assistant to generate new practice questions, and to review your query. Do not let it write the answer for you, because you will not have it in the interview room.
New to SQL? Start with our data analyst roadmap for 2026, which shows where SQL fits alongside Excel, Power BI and Python.
Practise with a mentor. In our Data Analytics with AI course, SQL is a three-week module built around business case studies like these, with weekly reviews and mock interviews at the end. Live online, from anywhere in India.