Advanced SQL Interview Questions
Advanced SQL Interview Questions & Answers
1️⃣ Second Highest Salary (No TOP/LIMIT/RANK)
You can use a subquery to find the second highest salary:
SELECT MAX(salary) AS SecondHighest
FROM employees
WHERE salary < (SELECT MAX(salary) FROM employees);
This finds the maximum salary less than the overall maximum.
2️⃣ WHERE vs HAVING
WHERE filters rows before aggregation.
HAVING filters groups after aggregation.
HAVING filters groups after aggregation.
SELECT department, AVG(salary)
FROM employees
WHERE salary > 3000
GROUP BY department
HAVING AVG(salary) > 5000;
3️⃣ Detect & Delete Duplicates
Detect duplicates:
SELECT name, COUNT(*)
FROM employees
GROUP BY name
HAVING COUNT(*) > 1;
Delete duplicates (keep one copy):
DELETE FROM employees
WHERE rowid NOT IN (
SELECT MIN(rowid)
FROM employees
GROUP BY name
);
4️⃣ Correlated vs Uncorrelated Subqueries
Uncorrelated subquery runs independently.
Correlated subquery depends on outer query row.
Correlated subquery depends on outer query row.
SELECT e1.*
FROM employees e1
WHERE salary > (
SELECT AVG(salary)
FROM employees e2
WHERE e1.department_id = e2.department_id
);
5️⃣ Running Totals & Moving Averages
SELECT employee_id, salary,
SUM(salary) OVER (ORDER BY employee_id) AS RunningTotal,
AVG(salary) OVER (ORDER BY employee_id ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS MovingAvg
FROM employees;
6️⃣ Handling NULLs in Aggregates
COUNT, SUM, and AVG ignore NULLs. Use COALESCE to replace NULLs.
SELECT COUNT(salary), SUM(COALESCE(salary,0)), AVG(COALESCE(salary,0))
FROM employees;
7️⃣ UNION vs UNION ALL
UNION removes duplicates.
UNION ALL keeps duplicates (faster).
UNION ALL keeps duplicates (faster).
SELECT name FROM employees
UNION
SELECT name FROM managers;
8️⃣ Pivot & Unpivot
Pivot rows into columns and vice versa:
-- Pivot
SELECT department,
SUM(CASE WHEN gender='M' THEN 1 ELSE 0 END) AS Male,
SUM(CASE WHEN gender='F' THEN 1 ELSE 0 END) AS Female
FROM employees
GROUP BY department;
-- Unpivot
SELECT department, gender, cnt
FROM (
SELECT department,
COUNT(CASE WHEN gender='M' THEN 1 END) AS Male,
COUNT(CASE WHEN gender='F' THEN 1 END) AS Female
FROM employees
GROUP BY department
) t
UNPIVOT (cnt FOR gender IN (Male, Female)) u;
9️⃣ Recursive Queries for Hierarchies
WITH RECURSIVE EmployeeHierarchy AS (
SELECT employee_id, manager_id, name
FROM employees
WHERE manager_id IS NULL
UNION ALL
SELECT e.employee_id, e.manager_id, e.name
FROM employees e
INNER JOIN EmployeeHierarchy h
ON e.manager_id = h.employee_id
)
SELECT * FROM EmployeeHierarchy;
🔟 Gaps & Islands in Dates
SELECT id, date_column,
date_column - ROW_NUMBER() OVER (ORDER BY date_column) AS grp
FROM events;
-- Then group by grp to find islands
1️⃣1️⃣ DELETE vs TRUNCATE vs DROP
DELETE: Removes rows (can use WHERE).
TRUNCATE: Removes all rows, faster, cannot use WHERE.
DROP: Removes entire table.
TRUNCATE: Removes all rows, faster, cannot use WHERE.
DROP: Removes entire table.
1️⃣2️⃣ SCD Type 2 Logic
MERGE INTO dim_customer d
USING staging_customer s
ON d.customer_id = s.customer_id
WHEN MATCHED AND d.current_flag = 1 AND d.attribute <> s.attribute
THEN UPDATE SET d.current_flag = 0, d.end_date = CURRENT_DATE
WHEN NOT MATCHED THEN
INSERT (customer_id, attribute, start_date, end_date, current_flag)
VALUES (s.customer_id, s.attribute, CURRENT_DATE, NULL, 1);
1️⃣3️⃣ Employees With Higher Salary Than Manager
SELECT e.name AS Employee, m.name AS Manager
FROM employees e
JOIN employees m ON e.manager_id = m.employee_id
WHERE e.salary > m.salary;
1️⃣4️⃣ Types of JOINs
INNER JOIN: Only matching rows.
LEFT JOIN: All left rows + matches.
RIGHT JOIN: All right rows + matches.
FULL OUTER JOIN: All rows from both sides.
LEFT JOIN: All left rows + matches.
RIGHT JOIN: All right rows + matches.
FULL OUTER JOIN: All rows from both sides.
1️⃣5️⃣ Calculate Median Without MEDIAN()
SELECT AVG(salary) AS Median
FROM (
SELECT salary,
ROW_NUMBER() OVER (ORDER BY salary) AS rn,
COUNT(*) OVER() AS cnt
FROM employees
) t
WHERE rn IN (FLOOR((cnt+1)/2), CEIL((cnt+1)/2));
1️⃣6️⃣ Nth Highest Value (No Window)
SELECT DISTINCT salary
FROM employees e1
WHERE (N-1) = (
SELECT COUNT(DISTINCT salary)
FROM employees e2
WHERE e2.salary > e1.salary
);
1️⃣7️⃣ CROSS APPLY vs OUTER APPLY
CROSS APPLY: Like INNER JOIN with table-valued function.
OUTER APPLY: Like LEFT JOIN with table-valued function.
OUTER APPLY: Like LEFT JOIN with table-valued function.
SELECT e.name, d.*
FROM employees e
CROSS APPLY fnGetDetails(e.id) d;
1️⃣8️⃣ Identify & Remove Orphan Records
SELECT c.*
FROM child c
LEFT JOIN parent p ON c.parent_id = p.id
WHERE p.id IS NULL;
DELETE FROM child
WHERE parent_id NOT IN (SELECT id FROM parent);
1️⃣9️⃣ Clustered vs Non-Clustered Indexes
Clustered index: Sorts and stores rows physically in table.
Non-clustered index: Separate structure with pointers to data.
Non-clustered index: Separate structure with pointers to data.
2️⃣0️⃣ Query Optimization Tips
- Create proper indexes (on join/filter columns).
- Use covering indexes to reduce lookups.
- Check execution plan for bottlenecks.
- Avoid
SELECT *
. - Partition large tables.