0% found this document useful (0 votes)
4 views12 pages

SQL Interview Handbook

This document is a comprehensive handbook of SQL interview questions categorized by table schemas, including Employee/Department, Customer/Orders, and Product/Sales. Each section contains various SQL queries along with their patterns, designed to help users practice and enhance their SQL skills. The queries cover a range of topics such as filtering, aggregation, and window functions.

Uploaded by

sharvanimone
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
4 views12 pages

SQL Interview Handbook

This document is a comprehensive handbook of SQL interview questions categorized by table schemas, including Employee/Department, Customer/Orders, and Product/Sales. Each section contains various SQL queries along with their patterns, designed to help users practice and enhance their SQL skills. The queries cover a range of topics such as filtering, aggregation, and window functions.

Uploaded by

sharvanimone
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

SQL Interview Questions by Table Schema

This handbook groups SQL interview questions based on related table schemas. Practice section by section to build
stronger SQL intuition.

1. Employee/Department Questions

Table Schema
Employee(
employee_id INT,
name VARCHAR(100),
salary INT,
dept_id INT,
manager_id INT,
hire_date DATE
)

Department(
dept_id INT,
dept_name VARCHAR(100)
)

Interview Questions

Q1. Find employees whose salary is above company average

SQL: SELECT * FROM Employee WHERE salary > (SELECT AVG(salary) FROM Employee);

Pattern: Subquery + Aggregate

Q2. Find employees who joined in the last 30 days

SQL: SELECT * FROM Employee WHERE hire_date >= CURRENT_DATE - INTERVAL 30 DAY;

Pattern: Date Function + Filtering

Q3. Find employees who have the same salary

SQL: SELECT salary, COUNT(*) AS cnt FROM Employee GROUP BY salary HAVING COUNT(*) > 1;

Pattern: GROUP BY + HAVING

Q4. Display employees who earn more than their manager

SQL: SELECT e.* FROM Employee e JOIN Employee m ON e.manager_id = m.employee_id WHERE [Link] > [Link];

Pattern: Self Join + Filtering

Q5. Split full name into first and last name


SQL: SELECT SUBSTRING_INDEX(full_name, ' ', 1) AS first_name, SUBSTRING_INDEX(full_name, ' ', -1) AS last_name
FROM Employee;

Pattern: String Function

Q6. Calculate median salary in each department

SQL: SELECT dept_id, AVG(salary) AS median_salary FROM (SELECT dept_id, salary, ROW_NUMBER() OVER
(PARTITION BY dept_id ORDER BY salary) AS rn, COUNT(*) OVER (PARTITION BY dept_id) AS cnt FROM Employee) t
WHERE rn IN (FLOOR((cnt+1)/2), CEIL((cnt+1)/2)) GROUP BY dept_id;

Pattern: Window Function + Subquery

Q7. Display employees who report to managers in different departments

SQL: SELECT e.* FROM Employee e JOIN Employee m ON e.manager_id = m.employee_id WHERE e.dept_id <>
m.dept_id;

Pattern: Self Join + Filtering

Q8. Display employees hired on the same date

SQL: SELECT hire_date, COUNT(*) AS emp_count FROM Employee GROUP BY hire_date HAVING COUNT(*) > 1;

Pattern: GROUP BY + HAVING

Q9. Find Nth highest salary without using LIMIT

SQL: SELECT DISTINCT salary FROM Employee e1 WHERE (SELECT COUNT(DISTINCT salary) FROM Employee e2
WHERE [Link] > [Link]) = N-1;

Pattern: Correlated Subquery

Q10. Find the second highest salary from the Employee table

SQL: SELECT MAX(salary) AS second_highest FROM Employee WHERE salary < (SELECT MAX(salary) FROM
Employee);

Pattern: Subquery + Aggregate

Q11. Display top 3 highest paid employees in each department

SQL: SELECT * FROM (SELECT e.*, DENSE_RANK() OVER (PARTITION BY dept_id ORDER BY salary DESC) AS rnk
FROM Employee e) t WHERE rnk <= 3;

Pattern: Window Function + PARTITION BY

Q12. Find gaps in sequence of IDs

SQL: SELECT [Link] + 1 AS gap_start FROM Employee e1 WHERE NOT EXISTS (SELECT 1 FROM Employee e2 WHERE
[Link] = [Link] + 1) AND [Link] < (SELECT MAX(id) FROM Employee);

Pattern: Subquery + NOT EXISTS


Q13. Find names that contain numbers

SQL: SELECT * FROM Employee WHERE name REGEXP '[0-9]';

Pattern: String Pattern + Filtering

Q14. Calculate salary difference between employee and department average

SQL: SELECT e.*, [Link] - AVG([Link]) OVER (PARTITION BY dept_id) AS diff FROM Employee e;

Pattern: Window Function + Aggregate

Q15. Find the highest salary in each department

SQL: SELECT dept_id, MAX(salary) AS highest_salary FROM Employee GROUP BY dept_id;

Pattern: GROUP BY + Aggregate

Q16. Find employees hired in same month and year

SQL: SELECT YEAR(hire_date) AS year, MONTH(hire_date) AS month, COUNT(*) AS count FROM Employee GROUP BY
YEAR(hire_date), MONTH(hire_date) HAVING COUNT(*) > 1;

Pattern: Date Function + GROUP BY + HAVING

Q17. Display employee hierarchy using recursive CTE

SQL: WITH RECURSIVE emp_hierarchy AS (SELECT employee_id, name, manager_id, 1 AS level FROM Employee
WHERE manager_id IS NULL UNION ALL SELECT e.employee_id, [Link], e.manager_id, [Link]+1 FROM Employee e
JOIN emp_hierarchy eh ON e.manager_id = eh.employee_id) SELECT * FROM emp_hierarchy;

Pattern: Recursive CTE + JOIN

Q18. Find employees with anniversaries in current month

SQL: SELECT * FROM Employee WHERE MONTH(hire_date) = MONTH(CURRENT_DATE);

Pattern: Date Function + Filtering

Q19. Find overlapping employee work shifts

SQL: SELECT s1.employee_id, s2.employee_id FROM Shift s1 JOIN Shift s2 ON s1.shift_id <> s2.shift_id AND
s1.start_time < s2.end_time AND s1.end_time > s2.start_time;

Pattern: Self Join + Time Logic

Q20. Delete duplicate rows keeping only one record

SQL: DELETE FROM Employee WHERE id NOT IN (SELECT min_id FROM (SELECT MIN(id) AS min_id FROM Employee
GROUP BY name, salary, department) AS t);

Pattern: Subquery + DELETE

Q21. Find departments with no employees


SQL: SELECT d.* FROM Department d LEFT JOIN Employee e ON d.dept_id = e.dept_id WHERE e.employee_id IS NULL;

Pattern: LEFT JOIN + NULL check

Q22. Find departments where all employees earn above 50k

SQL: SELECT dept_id FROM Employee GROUP BY dept_id HAVING MIN(salary) > 50000;

Pattern: GROUP BY + HAVING + Aggregate

2. Customer/Orders Questions

Table Schema
Customer(
customer_id INT,
name VARCHAR(100),
email VARCHAR(100),
phone VARCHAR(20)
)

Orders(
order_id INT,
customer_id INT,
order_amount DECIMAL(10,2),
order_date DATE
)

Interview Questions

Q1. Find longest streak of consecutive days with orders

SQL: WITH dates AS (SELECT DISTINCT order_date FROM Orders), numbered AS (SELECT order_date,
ROW_NUMBER() OVER (ORDER BY order_date) - ROW_NUMBER() OVER (PARTITION BY 1 ORDER BY order_date)
AS grp FROM dates) SELECT MAX(cnt) FROM (SELECT COUNT(*) AS cnt FROM numbered GROUP BY grp) t;

Pattern: Window Function + Subquery

Q2. Calculate average time between orders per customer

SQL: SELECT customer_id, AVG(DATEDIFF(order_date, prev_order)) AS avg_days FROM (SELECT customer_id,


order_date, LAG(order_date) OVER (PARTITION BY customer_id ORDER BY order_date) AS prev_order FROM Orders) t
WHERE prev_order IS NOT NULL GROUP BY customer_id;

Pattern: Window Function + Date + Subquery

Q3. Display customers with more than 5 orders

SQL: SELECT customer_id, COUNT(*) AS order_count FROM Orders GROUP BY customer_id HAVING COUNT(*) > 5;

Pattern: GROUP BY + HAVING

Q4. Calculate churn rate by cohort


SQL: SELECT cohort_month, COUNT(CASE WHEN last_order_month < cohort_month + INTERVAL 3 MONTH THEN 1
END)*100.0/COUNT(*) AS churn_rate FROM CustomerCohort GROUP BY cohort_month;

Pattern: CASE + Date + Aggregate

Q5. Find the customer with highest total purchase amount

SQL: SELECT customer_id, SUM(order_amount) AS total FROM Orders GROUP BY customer_id ORDER BY total DESC
LIMIT 1;

Pattern: GROUP BY + Aggregate + ORDER BY

Q6. Display total order amount for each customer

SQL: SELECT customer_id, SUM(order_amount) AS total FROM Orders GROUP BY customer_id;

Pattern: GROUP BY + Aggregate

Q7. Calculate customer retention rate month-over-month

SQL: SELECT [Link], COUNT(DISTINCT m_next.customer_id)*100.0/COUNT(DISTINCT m.customer_id) AS retention


FROM (SELECT DATE_TRUNC('month', order_date) AS month, customer_id FROM Orders) m LEFT JOIN (SELECT
DATE_TRUNC('month', order_date) AS month, customer_id FROM Orders) m_next ON m.customer_id =
m_next.customer_id AND m_next.month = [Link] + INTERVAL '1 month' GROUP BY [Link];

Pattern: Self Join + Date + Aggregate

Q8. Extract domain from email addresses

SQL: SELECT email, SUBSTRING(email, POSITION('@' IN email) + 1) AS domain FROM Customer;

Pattern: String Function

Q9. Calculate business days between two dates

SQL: SELECT order_id, DATEDIFF(delivery_date, order_date) - (WEEK(delivery_date) - WEEK(order_date))*2 AS


business_days FROM Orders;

Pattern: Date Function + Calculation

Q10. Find all orders placed on weekends

SQL: SELECT * FROM Orders WHERE DAYOFWEEK(order_date) IN (1,7);

Pattern: Date Function + Filtering

Q11. Find phone numbers not matching pattern

SQL: SELECT * FROM Customer WHERE phone NOT REGEXP '^[0-9]{3}-[0-9]{3}-[0-9]{4}$';

Pattern: String Pattern + Filtering

Q12. Find all emails with invalid format


SQL: SELECT * FROM Customer WHERE email NOT LIKE '%@%.%' OR email LIKE '%@%@%';

Pattern: String Pattern + Filtering

Q13. Display orders grouped by fiscal quarter

SQL: SELECT QUARTER(order_date) AS quarter, YEAR(order_date) AS year, COUNT(*) FROM Orders GROUP BY
YEAR(order_date), QUARTER(order_date);

Pattern: Date Function + GROUP BY

Q14. Find customers who placed orders in consecutive months

SQL: SELECT DISTINCT o1.customer_id FROM Orders o1 JOIN Orders o2 ON o1.customer_id = o2.customer_id AND
MONTH(o2.order_date) = MONTH(o1.order_date) + 1 AND YEAR(o2.order_date) = YEAR(o1.order_date);

Pattern: Self Join + Date Function

Q15. Find average order value per customer

SQL: SELECT customer_id, AVG(order_amount) AS avg_order FROM Orders GROUP BY customer_id;

Pattern: GROUP BY + Aggregate

Q16. Find customers who ordered the same product more than once

SQL: SELECT o.customer_id, od.product_id, COUNT(*) AS times FROM Orders o JOIN OrderDetails od ON o.order_id =
od.order_id GROUP BY o.customer_id, od.product_id HAVING COUNT(*) > 1;

Pattern: JOIN + GROUP BY + HAVING

Q17. Find first and last order date for each customer

SQL: SELECT customer_id, MIN(order_date) AS first_order, MAX(order_date) AS last_order FROM Orders GROUP BY
customer_id;

Pattern: GROUP BY + Aggregate

Q18. Find duplicate records across multiple columns

SQL: SELECT name, email, COUNT(*) FROM Customer GROUP BY name, email HAVING COUNT(*) > 1;

Pattern: GROUP BY + HAVING

Q19. Find customers who purchased in Q1 but not Q2

SQL: SELECT DISTINCT customer_id FROM Orders WHERE QUARTER(order_date)=1 AND customer_id NOT IN
(SELECT customer_id FROM Orders WHERE QUARTER(order_date)=2);

Pattern: Subquery + NOT IN + Date

Q20. Find customers with increasing order values


SQL: SELECT customer_id FROM (SELECT customer_id, order_date, order_amount, LAG(order_amount) OVER
(PARTITION BY customer_id ORDER BY order_date) AS prev_amount FROM Orders) t WHERE order_amount >
prev_amount GROUP BY customer_id HAVING COUNT(*) = (SELECT COUNT(*) - 1 FROM Orders o WHERE
o.customer_id = customer_id);

Pattern: Window Function + Subquery + HAVING

Q21. List orders placed in the last quarter

SQL: SELECT * FROM Orders WHERE order_date >= DATE_SUB(DATE_SUB(CURRENT_DATE, INTERVAL


(MONTH(CURRENT_DATE)-1) MONTH), INTERVAL 3 MONTH);

Pattern: Date Function + Filtering

Q22. Find customers who have never placed an order

SQL: SELECT c.* FROM Customer c LEFT JOIN Orders o ON c.customer_id = o.customer_id WHERE o.order_id IS NULL;

Pattern: LEFT JOIN + NULL check

Q23. Find customers who ordered all available products

SQL: SELECT customer_id FROM Orders o JOIN OrderDetails od ON o.order_id = od.order_id GROUP BY customer_id
HAVING COUNT(DISTINCT od.product_id) = (SELECT COUNT(*) FROM Product);

Pattern: JOIN + GROUP BY + HAVING

Q24. Display month-over-month growth in orders

SQL: SELECT month, orders, orders - COALESCE(prev_orders,0) AS growth FROM (SELECT MONTH(order_date) AS
month, COUNT(*) AS orders FROM Orders GROUP BY MONTH(order_date)) t LEFT JOIN (SELECT MONTH(order_date)
AS month, COUNT(*) AS prev_orders FROM Orders GROUP BY MONTH(order_date)) p ON [Link] = [Link] + 1;

Pattern: Window Function + Date + Aggregate

3. Product/Sales Questions

Table Schema
Product(
product_id INT,
category VARCHAR(100),
price DECIMAL(10,2),
stock_quantity INT
)

OrderDetails(
order_id INT,
product_id INT,
quantity INT,
price DECIMAL(10,2)
)

Sales(
product_id INT,
region VARCHAR(50),
sales DECIMAL(10,2),
order_date DATE
)
Interview Questions

Q1. Display products never sold in a specific region

SQL: SELECT p.* FROM Product p WHERE p.product_id NOT IN (SELECT DISTINCT product_id FROM Sales WHERE
region = 'North');

Pattern: Subquery + NOT IN + Filtering

Q2. Find products with price changes

SQL: SELECT p1.product_id, [Link] AS old_price, [Link] AS new_price FROM ProductHistory p1 JOIN ProductHistory
p2 ON p1.product_id = p2.product_id AND p1.effective_date < p2.effective_date WHERE [Link] <> [Link];

Pattern: Self Join + Filtering

Q3. Display products with declining inventory

SQL: SELECT product_id FROM Inventory i WHERE i.stock_date = CURRENT_DATE AND [Link] < (SELECT quantity
FROM Inventory i2 WHERE i2.product_id = i.product_id AND i2.stock_date = CURRENT_DATE - INTERVAL 1 MONTH);

Pattern: Correlated Subquery + Date

Q4. Find products with sales greater than average

SQL: SELECT product_id, SUM(quantity * price) AS sales FROM OrderDetails GROUP BY product_id HAVING
SUM(quantity * price) > (SELECT AVG(total) FROM (SELECT SUM(quantity * price) AS total FROM OrderDetails GROUP
BY product_id) AS t);

Pattern: Subquery + GROUP BY + HAVING

Q5. Find products that are out of stock

SQL: SELECT * FROM Product WHERE stock_quantity = 0 OR stock_quantity IS NULL;

Pattern: Filtering + NULL check

Q6. Display products never returned

SQL: SELECT p.* FROM Product p WHERE p.product_id NOT IN (SELECT product_id FROM Returns);

Pattern: Subquery + NOT IN

Q7. Display running difference from previous month

SQL: SELECT month, sales, sales - LAG(sales) OVER (ORDER BY month) AS diff FROM MonthlySales;

Pattern: Window Function + Calculation

Q8. Calculate running total of sales by date

SQL: SELECT order_date, order_amount, SUM(order_amount) OVER (ORDER BY order_date ROWS BETWEEN
UNBOUNDED PRECEDING AND CURRENT ROW) AS running_total FROM Orders;
Pattern: Window Function + Aggregate

Q9. Find products ordered together frequently

SQL: SELECT od1.product_id AS product1, od2.product_id AS product2, COUNT(*) AS frequency FROM OrderDetails od1
JOIN OrderDetails od2 ON od1.order_id = od2.order_id AND od1.product_id < od2.product_id GROUP BY od1.product_id,
od2.product_id ORDER BY frequency DESC;

Pattern: Self Join + GROUP BY

Q10. Display year-to-date sales by month

SQL: SELECT MONTH(order_date) AS month, SUM(order_amount) AS monthly_sales, SUM(SUM(order_amount)) OVER


(ORDER BY MONTH(order_date)) AS ytd_sales FROM Orders WHERE YEAR(order_date)=YEAR(CURRENT_DATE)
GROUP BY MONTH(order_date);

Pattern: Window Function + Date + Aggregate

Q11. Calculate percentage of total sales by region

SQL: SELECT region, SUM(sales)*100.0/(SELECT SUM(sales) FROM Sales) AS percentage FROM Sales GROUP BY
region;

Pattern: Subquery + Aggregate + Calculation

Q12. List products in at least 10 different orders

SQL: SELECT product_id, COUNT(DISTINCT order_id) AS order_count FROM OrderDetails GROUP BY product_id
HAVING COUNT(DISTINCT order_id) >= 10;

Pattern: GROUP BY + HAVING + DISTINCT

Q13. Display year-over-year sales comparison

SQL: SELECT YEAR(order_date) AS year, SUM(order_amount) AS sales, LAG(SUM(order_amount)) OVER (ORDER BY


YEAR(order_date)) AS prev_year FROM Orders GROUP BY YEAR(order_date);

Pattern: Window Function + Date + Aggregate

Q14. Calculate cumulative sum partitioned by category

SQL: SELECT category, order_date, sales, SUM(sales) OVER (PARTITION BY category ORDER BY order_date) AS
cumulative FROM Sales;

Pattern: Window Function + PARTITION BY

Q15. Find product category with highest revenue

SQL: SELECT [Link], SUM([Link] * [Link]) AS revenue FROM Product p JOIN OrderDetails od ON p.product_id
= od.product_id GROUP BY [Link] ORDER BY revenue DESC LIMIT 1;

Pattern: JOIN + GROUP BY + Aggregate

Q16. Find products with declining sales trend


SQL: SELECT product_id FROM (SELECT product_id, MONTH(order_date) AS month, SUM(quantity) AS sales,
LAG(SUM(quantity)) OVER (PARTITION BY product_id ORDER BY MONTH(order_date)) AS prev_sales FROM
OrderDetails GROUP BY product_id, MONTH(order_date)) t WHERE sales < prev_sales;

Pattern: Window Function + Subquery + Filtering

Q17. Find products sold in all regions

SQL: SELECT product_id FROM Sales GROUP BY product_id HAVING COUNT(DISTINCT region) = (SELECT
COUNT(DISTINCT region) FROM Sales);

Pattern: GROUP BY + HAVING + Subquery

Q18. Display top 5 best-selling products

SQL: SELECT product_id, SUM(quantity) AS total_sold FROM OrderDetails GROUP BY product_id ORDER BY total_sold
DESC LIMIT 5;

Pattern: GROUP BY + Aggregate + ORDER BY

Q19. Display pivot table of sales by product and month

SQL: SELECT product_id, SUM(CASE WHEN MONTH(order_date)=1 THEN quantity ELSE 0 END) AS Jan, SUM(CASE
WHEN MONTH(order_date)=2 THEN quantity ELSE 0 END) AS Feb FROM OrderDetails od JOIN Orders o ON od.order_id
= o.order_id GROUP BY product_id;

Pattern: CASE + GROUP BY + Aggregate

Q20. List products that have never been ordered

SQL: SELECT p.* FROM Product p LEFT JOIN OrderDetails od ON p.product_id = od.product_id WHERE od.order_id IS
NULL;

Pattern: LEFT JOIN + NULL check

Q21. Display top 3 products by revenue in each category

SQL: SELECT * FROM (SELECT category, product_id, revenue, ROW_NUMBER() OVER (PARTITION BY category
ORDER BY revenue DESC) AS rn FROM ProductRevenue) t WHERE rn <= 3;

Pattern: Window Function + PARTITION BY

4. User/Activity Questions

Table Schema
UserLogin(
user_id INT,
login_date DATE
)

UserActivity(
user_id INT,
source VARCHAR(50),
purchased INT
)

Interview Questions

Q1. Find users who logged in on consecutive days

SQL: SELECT DISTINCT u1.user_id FROM UserLogin u1 JOIN UserLogin u2 ON u1.user_id = u2.user_id AND
u2.login_date = u1.login_date + INTERVAL 1 DAY;

Pattern: Self Join + Date Logic

Q2. Calculate conversion rate by traffic source

SQL: SELECT source, COUNT(CASE WHEN purchased=1 THEN 1 END)*100.0/COUNT(*) AS conversion_rate FROM
UserActivity GROUP BY source;

Pattern: CASE + GROUP BY + Aggregate

5. Student/Academic Questions

Table Schema
Student(
student_id INT,
name VARCHAR(100),
class_id INT,
score INT
)

Interview Questions

Q1. List all students who scored above class average

SQL: SELECT s.student_id, [Link], [Link] FROM Student s WHERE [Link] > (SELECT AVG(score) FROM Student
WHERE class_id = s.class_id);

Pattern: Correlated Subquery + Filtering

6. Transaction Questions

Table Schema
Transactions(
transaction_id INT,
account_id INT,
amount DECIMAL(10,2),
transaction_date DATE,
transaction_time TIME
)

Interview Questions
Q1. Find busiest hour of day for transactions

SQL: SELECT HOUR(transaction_time) AS hour, COUNT(*) AS transaction_count FROM Transactions GROUP BY


HOUR(transaction_time) ORDER BY transaction_count DESC LIMIT 1;

Pattern: Date Function + GROUP BY

Q2. Find transactions that exceed daily limit

SQL: SELECT account_id, transaction_date, SUM(amount) AS daily_total FROM Transactions GROUP BY account_id,
transaction_date HAVING SUM(amount) > 10000;

Pattern: GROUP BY + HAVING + Aggregate

7. Booking/Reservation Questions

Table Schema
Booking(
booking_id INT,
room_id INT,
start_date DATE,
end_date DATE
)

Interview Questions

Q1. Find overlapping date ranges in bookings

SQL: SELECT b1.* FROM Booking b1 JOIN Booking b2 ON b1.room_id = b2.room_id AND b1.booking_id <> b2.booking_id
AND b1.start_date <= b2.end_date AND b1.end_date >= b2.start_date;

Pattern: Self Join + Date Logic

You might also like