20 SQL Interview Questions for Data Engineers: Real Problems, Step-by-Step Solutions, and the Thinking Process Behind Each Answer
You have learned every SQL concept — joins, window functions, subqueries, CTEs, GROUP BY, MERGE, indexes, transactions. But knowing the concepts and solving interview problems under pressure are two different skills.
The gap is not knowledge — it is PATTERN RECOGNITION. Interviewers do not ask “explain ROW_NUMBER.” They ask “find the second highest salary per department.” You need to instantly recognize: “This is a top-N-per-group problem → window function → ROW_NUMBER with PARTITION BY.”
This post gives you 20 real interview problems, organized from basic to advanced. For each one, I show the thinking process FIRST (how to recognize the pattern), then the solution, then common follow-up questions the interviewer might ask. Practice these, and you will recognize 90% of SQL interview patterns on sight.
The Sample Database
All questions use this database. Run the CREATE + INSERT statements to practice along:
CREATE TABLE employees (
emp_id INT PRIMARY KEY,
name VARCHAR(50),
department VARCHAR(50),
salary DECIMAL(10,2),
manager_id INT NULL,
hire_date DATE
);
INSERT INTO employees VALUES
(1, 'Naveen', 'Engineering', 105000, NULL, '2022-01-15'),
(2, 'Shrey', 'Engineering', 95000, 1, '2023-03-20'),
(3, 'Vrushab', 'Engineering', 92000, 1, '2023-06-10'),
(4, 'Vishnu', 'Analytics', 91000, NULL, '2023-09-01'),
(5, 'Ravi', 'Analytics', 85000, 4, '2024-01-05'),
(6, 'Priya', 'Analytics', 85000, 4, '2023-07-15'),
(7, 'Anita', 'Sales', 78000, NULL, '2024-03-15'),
(8, 'Deepak', 'Sales', 82000, 7, '2024-05-01'),
(9, 'Kavya', 'Sales', 78000, 7, '2024-02-10'),
(10, 'Manoj', 'Sales', 75000, 7, '2023-11-20');
CREATE TABLE orders (
order_id INT PRIMARY KEY,
customer_id INT,
product VARCHAR(50),
amount DECIMAL(10,2),
order_date DATE
);
INSERT INTO orders VALUES
(101, 1, 'Laptop', 999.99, '2025-01-15'),
(102, 2, 'Mouse', 29.99, '2025-01-20'),
(103, 1, 'Keyboard', 79.99, '2025-02-10'),
(104, 3, 'Monitor', 399.99, '2025-02-15'),
(105, 1, 'Laptop', 999.99, '2025-03-01'),
(106, 4, 'Laptop', 999.99, '2025-03-10'),
(107, 2, 'Webcam', 59.99, '2025-03-15'),
(108, 5, 'Laptop', 999.99, '2025-04-01'),
(109, 3, 'Keyboard', 79.99, '2025-04-10'),
(110, 1, 'Mouse', 29.99, '2025-04-20'),
(111, 6, 'Monitor', 399.99, '2025-05-01'),
(112, 2, 'Laptop', 999.99, '2025-05-10');
CREATE TABLE customers (
customer_id INT PRIMARY KEY,
name VARCHAR(50),
city VARCHAR(50),
signup_date DATE
);
INSERT INTO customers VALUES
(1, 'Alice', 'Toronto', '2024-01-01'),
(2, 'Bob', 'Mumbai', '2024-03-15'),
(3, 'Carol', 'Toronto', '2024-06-01'),
(4, 'David', 'Delhi', '2024-09-10'),
(5, 'Eve', 'Toronto', '2025-01-01'),
(6, 'Frank', 'Mumbai', '2025-02-15'),
(7, 'Grace', 'Vancouver', '2025-03-01');
Table of Contents
- Q1: Second Highest Salary (Classic)
- Q2: Nth Highest Salary Per Department
- Q3: Employees Earning More Than Their Manager
- Q4: Duplicate Detection
- Q5: Consecutive Days with Orders
- Q6: Customers Who Never Ordered
- Q7: Department with Highest Average Salary
- Q8: Running Total
- Q9: Year-over-Year Growth
- Q10: Top 3 Products by Revenue
- Q11: Employees Hired in the Same Month
- Q12: Find Gaps in Sequential IDs
- Q13: Customers Who Ordered Every Month
- Q14: Percentage of Total
- Q15: Self-Join: Find Pairs
- Q16: Delete Duplicates (Keep One)
- Q17: Moving Average
- Q18: Rank Without Gaps vs With Gaps
- Q19: Pivot Monthly Revenue by Product
- Q20: Complex Multi-CTE Business Question
- Pattern Recognition Cheat Sheet
- Final Tips for SQL Interviews
Q1: Second Highest Salary (Classic)
Question: Find the second highest salary in the company.
Think: “Second highest → need ranking → but just one value, not per group.”
-- Solution 1: Subquery with MAX
SELECT MAX(salary) AS second_highest
FROM employees
WHERE salary < (SELECT MAX(salary) FROM employees);
-- Logic: Find the max salary that is LESS than the overall max
-- Solution 2: DENSE_RANK (more flexible)
SELECT salary AS second_highest
FROM (
SELECT salary, DENSE_RANK() OVER (ORDER BY salary DESC) AS rnk
FROM employees
) ranked
WHERE rnk = 2;
-- Solution 3: OFFSET/FETCH (simplest)
SELECT DISTINCT salary AS second_highest
FROM employees
ORDER BY salary DESC
OFFSET 1 ROW FETCH NEXT 1 ROW ONLY;
Why DENSE_RANK and not ROW_NUMBER? If two employees share the highest salary, ROW_NUMBER would assign ranks 1 and 2 to them, making the next salary rank 3. DENSE_RANK correctly handles ties.
Follow-up: “What if there is no second highest (all salaries are the same)?” — The subquery approach returns NULL. DENSE_RANK returns no rows. Handle with COALESCE or an outer query.
Q2: Nth Highest Salary Per Department
Question: Find the 2nd highest salary IN EACH department.
Think: “Per department + ranking → PARTITION BY + DENSE_RANK.”
SELECT department, name, salary
FROM (
SELECT department, name, salary,
DENSE_RANK() OVER (PARTITION BY department ORDER BY salary DESC) AS rnk
FROM employees
) ranked
WHERE rnk = 2;
| department | name | salary |
|---|---|---|
| Analytics | Priya | 85000 |
| Analytics | Ravi | 85000 |
| Engineering | Shrey | 95000 |
| Sales | Anita | 78000 |
| Sales | Kavya | 78000 |
Priya and Ravi tie at rank 2 in Analytics. DENSE_RANK correctly returns both.
Pattern: Any “Nth per group” question → DENSE_RANK + PARTITION BY.
Q3: Employees Earning More Than Their Manager
Question: Find employees who earn more than their manager.
Think: “Compare employee to manager → self-join on manager_id.”
SELECT e.name AS employee, e.salary AS emp_salary,
m.name AS manager, m.salary AS mgr_salary
FROM employees e
JOIN employees m ON e.manager_id = m.emp_id
WHERE e.salary > m.salary;
Pattern: Any “compare row to a related row in the same table” → self-join.
Follow-up: “What if you also want employees with no manager?” — Use LEFT JOIN and handle NULLs.
Q4: Duplicate Detection
Question: Find customers who placed more than one order for the same product.
Think: “Same customer + same product more than once → GROUP BY + HAVING COUNT > 1.”
SELECT customer_id, product, COUNT(*) AS order_count
FROM orders
GROUP BY customer_id, product
HAVING COUNT(*) > 1
ORDER BY order_count DESC;
| customer_id | product | order_count |
|---|---|---|
| 1 | Laptop | 2 |
| 1 | Mouse | 2 |
| 3 | Keyboard | 2 |
Pattern: “Find duplicates” → GROUP BY the columns that define uniqueness + HAVING COUNT(*) > 1.
Follow-up: “Show the actual duplicate rows with all columns.”
SELECT o.*
FROM orders o
JOIN (
SELECT customer_id, product
FROM orders GROUP BY customer_id, product HAVING COUNT(*) > 1
) dupes ON o.customer_id = dupes.customer_id AND o.product = dupes.product
ORDER BY o.customer_id, o.product, o.order_date;
Q5: Consecutive Days with Orders
Question: Find customers who placed orders on 2 or more consecutive days.
Think: “Consecutive → need to compare current row with previous row → LAG.”
WITH order_with_prev AS (
SELECT customer_id, order_date,
LAG(order_date) OVER (PARTITION BY customer_id ORDER BY order_date) AS prev_order_date
FROM orders
)
SELECT DISTINCT customer_id
FROM order_with_prev
WHERE DATEDIFF(DAY, prev_order_date, order_date) = 1;
Pattern: “Consecutive” or “next/previous” → LAG or LEAD window functions.
Q6: Customers Who Never Ordered
Question: Find customers who have never placed an order.
Think: “In customers but NOT in orders → anti-join → LEFT JOIN IS NULL or NOT EXISTS.”
-- Solution 1: LEFT JOIN + IS NULL (most readable)
SELECT c.name, c.city
FROM customers c
LEFT JOIN orders o ON c.customer_id = o.customer_id
WHERE o.order_id IS NULL;
-- Solution 2: NOT EXISTS (often faster on large tables)
SELECT c.name, c.city
FROM customers c
WHERE NOT EXISTS (
SELECT 1 FROM orders o WHERE o.customer_id = c.customer_id
);
-- Solution 3: NOT IN (avoid if NULLs possible in subquery)
SELECT name, city FROM customers
WHERE customer_id NOT IN (SELECT DISTINCT customer_id FROM orders);
Result: Grace (customer_id = 7) — signed up but never ordered.
Pattern: “Never did X” or “without any Y” → LEFT JOIN IS NULL or NOT EXISTS. Always prefer NOT EXISTS over NOT IN (NULL-safe).
Q7: Department with Highest Average Salary
Question: Find the department with the highest average salary.
Think: “Aggregate per department → rank the aggregates → top 1.”
-- Solution 1: TOP 1
SELECT TOP 1 department, ROUND(AVG(salary), 0) AS avg_salary
FROM employees
GROUP BY department
ORDER BY avg_salary DESC;
-- Solution 2: CTE with RANK (handles ties)
WITH dept_avg AS (
SELECT department, AVG(salary) AS avg_salary,
RANK() OVER (ORDER BY AVG(salary) DESC) AS rnk
FROM employees
GROUP BY department
)
SELECT department, ROUND(avg_salary, 0) AS avg_salary
FROM dept_avg WHERE rnk = 1;
Follow-up: “What if two departments tie?” — TOP 1 returns only one. RANK returns both tied departments.
Q8: Running Total
Question: Calculate a running total of order amounts per customer, ordered by date.
Think: “Running total → SUM as a window function with ROWS frame.”
SELECT customer_id, order_date, product, amount,
SUM(amount) OVER (
PARTITION BY customer_id
ORDER BY order_date
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS running_total
FROM orders
ORDER BY customer_id, order_date;
| customer_id | order_date | product | amount | running_total |
|---|---|---|---|---|
| 1 | 2025-01-15 | Laptop | 999.99 | 999.99 |
| 1 | 2025-02-10 | Keyboard | 79.99 | 1079.98 |
| 1 | 2025-03-01 | Laptop | 999.99 | 2079.97 |
| 1 | 2025-04-20 | Mouse | 29.99 | 2109.96 |
Pattern: “Running total” or “cumulative sum” → SUM() OVER (ORDER BY … ROWS UNBOUNDED PRECEDING).
Q9: Year-over-Year Growth
Question: Calculate the month-over-month revenue growth percentage.
Think: “Compare this month with previous month → LAG on monthly aggregation.”
WITH monthly_revenue AS (
SELECT FORMAT(order_date, 'yyyy-MM') AS month,
SUM(amount) AS revenue
FROM orders
GROUP BY FORMAT(order_date, 'yyyy-MM')
),
with_prev AS (
SELECT month, revenue,
LAG(revenue) OVER (ORDER BY month) AS prev_revenue
FROM monthly_revenue
)
SELECT month, revenue, prev_revenue,
CASE WHEN prev_revenue IS NOT NULL AND prev_revenue != 0
THEN ROUND((revenue - prev_revenue) / prev_revenue * 100, 1)
ELSE NULL
END AS growth_pct
FROM with_prev
ORDER BY month;
Pattern: “Growth” or “compare with previous period” → aggregate first, then LAG.
Q10: Top 3 Products by Revenue
Question: Find the top 3 products by total revenue.
Think: “Aggregate by product → rank → top 3.”
SELECT TOP 3 product,
SUM(amount) AS total_revenue,
COUNT(*) AS order_count
FROM orders
GROUP BY product
ORDER BY total_revenue DESC;
| product | total_revenue | order_count |
|---|---|---|
| Laptop | 3999.96 | 4 |
| Monitor | 799.98 | 2 |
| Keyboard | 159.98 | 2 |
Follow-up: “Top 3 per city?” → Add PARTITION BY city with DENSE_RANK.
Q11: Employees Hired in the Same Month
Question: Find pairs of employees who were hired in the same month and year.
Think: “Pairs in the same table → self-join. Same month → YEAR and MONTH match.”
SELECT e1.name AS employee_1, e2.name AS employee_2,
FORMAT(e1.hire_date, 'yyyy-MM') AS hire_month
FROM employees e1
JOIN employees e2 ON YEAR(e1.hire_date) = YEAR(e2.hire_date)
AND MONTH(e1.hire_date) = MONTH(e2.hire_date)
AND e1.emp_id < e2.emp_id -- Avoid duplicates (A,B) and (B,A)
ORDER BY hire_month;
Why e1.emp_id < e2.emp_id? Without it, you get both (Naveen, Shrey) AND (Shrey, Naveen). The less-than condition keeps only one pair.
Pattern: “Find pairs” → self-join with < to avoid duplicates.
Q12: Find Gaps in Sequential IDs
Question: Find missing order IDs in the sequence (gaps where IDs were skipped or deleted).
Think: “Gaps → compare each ID to the next → LEAD.”
WITH with_next AS (
SELECT order_id,
LEAD(order_id) OVER (ORDER BY order_id) AS next_order_id
FROM orders
)
SELECT order_id AS gap_after,
next_order_id AS gap_before,
next_order_id - order_id - 1 AS missing_count
FROM with_next
WHERE next_order_id - order_id > 1;
Pattern: “Gaps” or “missing values” → LEAD to find the next value, subtract to find the gap.
Q13: Customers Who Ordered Every Month
Question: Find customers who placed at least one order in every month of 2025 (Jan-May).
Think: “Every month → count distinct months per customer → compare with total months.”
SELECT c.name, COUNT(DISTINCT FORMAT(o.order_date, 'yyyy-MM')) AS months_ordered
FROM customers c
JOIN orders o ON c.customer_id = o.customer_id
WHERE o.order_date >= '2025-01-01' AND o.order_date < '2026-01-01'
GROUP BY c.customer_id, c.name
HAVING COUNT(DISTINCT FORMAT(o.order_date, 'yyyy-MM')) = (
SELECT COUNT(DISTINCT FORMAT(order_date, 'yyyy-MM'))
FROM orders
WHERE order_date >= '2025-01-01' AND order_date < '2026-01-01'
);
Pattern: “Every X” or “all of” → count distinct occurrences per entity and compare with the total distinct count.
Q14: Percentage of Total
Question: Show each employee’s salary as a percentage of their department’s total salary.
Think: “Individual vs group total → window function SUM without ORDER BY.”
SELECT name, department, salary,
SUM(salary) OVER (PARTITION BY department) AS dept_total,
ROUND(salary * 100.0 / SUM(salary) OVER (PARTITION BY department), 1) AS pct_of_dept
FROM employees
ORDER BY department, salary DESC;
Pattern: “Percentage of total” or “ratio to group” → SUM() OVER (PARTITION BY group) as the denominator.
Q15: Self-Join: Find Pairs with Salary Difference < 5000
Question: Find all pairs of employees in the same department whose salary difference is less than 5000.
Think: “Pairs + same department + condition → self-join.”
SELECT e1.name AS employee_1, e2.name AS employee_2,
e1.department,
ABS(e1.salary - e2.salary) AS salary_diff
FROM employees e1
JOIN employees e2 ON e1.department = e2.department
AND e1.emp_id < e2.emp_id
WHERE ABS(e1.salary - e2.salary) < 5000
ORDER BY e1.department, salary_diff;
Q16: Delete Duplicates (Keep One)
Question: Delete duplicate rows from a table, keeping only the row with the lowest ID.
Think: “Identify duplicates with ROW_NUMBER → delete where row_num > 1.”
-- Step 1: Identify duplicates (preview first!)
SELECT *, ROW_NUMBER() OVER (
PARTITION BY customer_id, product
ORDER BY order_id ASC
) AS rn
FROM orders;
-- rn = 1 is the original, rn > 1 are duplicates
-- Step 2: Delete duplicates
WITH duplicates AS (
SELECT order_id, ROW_NUMBER() OVER (
PARTITION BY customer_id, product
ORDER BY order_id ASC
) AS rn
FROM orders
)
DELETE FROM duplicates WHERE rn > 1;
Pattern: “Delete duplicates keeping one” → ROW_NUMBER partitioned by duplicate columns, DELETE where rn > 1. Always preview with SELECT first.
Q17: Moving Average
Question: Calculate a 3-order moving average of order amounts per customer.
Think: “Moving average → AVG with ROWS BETWEEN frame.”
SELECT customer_id, order_date, amount,
ROUND(AVG(amount) OVER (
PARTITION BY customer_id
ORDER BY order_date
ROWS BETWEEN 2 PRECEDING AND CURRENT ROW
), 2) AS moving_avg_3
FROM orders
ORDER BY customer_id, order_date;
Pattern: “Moving average” or “rolling average” → AVG() OVER (ROWS BETWEEN N PRECEDING AND CURRENT ROW).
Q18: Rank Without Gaps vs With Gaps
Question: Rank employees by salary within each department. Show ROW_NUMBER, RANK, and DENSE_RANK side by side.
Think: “Compare ranking functions → show all three.”
SELECT department, name, salary,
ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) AS row_num,
RANK() OVER (PARTITION BY department ORDER BY salary DESC) AS rnk,
DENSE_RANK() OVER (PARTITION BY department ORDER BY salary DESC) AS dense_rnk
FROM employees
ORDER BY department, salary DESC;
| department | name | salary | row_num | rnk | dense_rnk |
|---|---|---|---|---|---|
| Analytics | Vishnu | 91000 | 1 | 1 | 1 |
| Analytics | Priya | 85000 | 2 | 2 | 2 |
| Analytics | Ravi | 85000 | 3 | 2 | 2 |
| Sales | Deepak | 82000 | 1 | 1 | 1 |
| Sales | Anita | 78000 | 2 | 2 | 2 |
| Sales | Kavya | 78000 | 3 | 2 | 2 |
| Sales | Manoj | 75000 | 4 | 4 | 3 |
Notice Manoj: ROW_NUMBER=4, RANK=4 (gap after ties), DENSE_RANK=3 (no gap).
Interviewer follow-up: “Which would you use for deduplication?” → ROW_NUMBER (unique per row). “For competition ranking?” → RANK (gaps). “For counting distinct salary levels?” → DENSE_RANK (no gaps).
Q19: Pivot Monthly Revenue by Product
Question: Show monthly revenue with products as columns.
Think: “Rows to columns → PIVOT or CASE WHEN.”
SELECT FORMAT(order_date, 'yyyy-MM') AS month,
SUM(CASE WHEN product = 'Laptop' THEN amount ELSE 0 END) AS Laptop,
SUM(CASE WHEN product = 'Monitor' THEN amount ELSE 0 END) AS Monitor,
SUM(CASE WHEN product = 'Keyboard' THEN amount ELSE 0 END) AS Keyboard,
SUM(CASE WHEN product = 'Mouse' THEN amount ELSE 0 END) AS Mouse,
SUM(CASE WHEN product = 'Webcam' THEN amount ELSE 0 END) AS Webcam,
SUM(amount) AS Total
FROM orders
GROUP BY FORMAT(order_date, 'yyyy-MM')
ORDER BY month;
Pattern: “Pivot” or “rows to columns” → SUM(CASE WHEN column = value THEN amount END) with GROUP BY.
Q20: Complex Multi-CTE Business Question
Question: Find the customers whose total spending is above the average customer spending, show their rank, and what percentage of total company revenue they represent.
Think: “Multiple steps → break into CTEs: customer totals → average → filter → rank → percentage.”
WITH customer_totals AS (
-- Step 1: Total spending per customer
SELECT c.customer_id, c.name, c.city,
SUM(o.amount) AS total_spent,
COUNT(o.order_id) AS order_count
FROM customers c
JOIN orders o ON c.customer_id = o.customer_id
GROUP BY c.customer_id, c.name, c.city
),
company_stats AS (
-- Step 2: Company-wide metrics
SELECT AVG(total_spent) AS avg_spending,
SUM(total_spent) AS total_revenue
FROM customer_totals
),
above_avg AS (
-- Step 3: Filter to above-average customers
SELECT ct.*,
RANK() OVER (ORDER BY ct.total_spent DESC) AS spending_rank,
ROUND(ct.total_spent * 100.0 / cs.total_revenue, 1) AS pct_of_revenue,
ROUND(cs.avg_spending, 2) AS avg_spending
FROM customer_totals ct
CROSS JOIN company_stats cs
WHERE ct.total_spent > cs.avg_spending
)
SELECT name, city, total_spent, order_count,
spending_rank, pct_of_revenue, avg_spending
FROM above_avg
ORDER BY spending_rank;
Pattern: Complex business questions → break into CTEs, each solving one piece. Name them clearly. Build step by step.
Pattern Recognition Cheat Sheet
| Interview Question Pattern | Technique | Key Function |
|---|---|---|
| “Nth highest” | Window + filter | DENSE_RANK() OVER (ORDER BY col DESC) |
| “Top N per group” | Window + filter | ROW_NUMBER() OVER (PARTITION BY grp ORDER BY col DESC) |
| “Compare to manager/parent” | Self-join | JOIN table t2 ON t1.parent_id = t2.id |
| “Find duplicates” | GROUP BY + HAVING | HAVING COUNT(*) > 1 |
| “Delete duplicates” | ROW_NUMBER + DELETE | DELETE WHERE rn > 1 |
| “Never did X” | Anti-join | LEFT JOIN ... WHERE id IS NULL or NOT EXISTS |
| “Consecutive days/rows” | LAG/LEAD | LAG(date) OVER (ORDER BY date) |
| “Running total” | Window SUM | SUM() OVER (ORDER BY date ROWS UNBOUNDED PRECEDING) |
| “Moving average” | Window AVG | AVG() OVER (ROWS BETWEEN N PRECEDING AND CURRENT ROW) |
| “Growth / change” | LAG on aggregation | LAG(metric) OVER (ORDER BY period) |
| “Percentage of total” | Window SUM denominator | val / SUM(val) OVER (PARTITION BY grp) |
| “Find pairs” | Self-join with < |
JOIN table t2 ON t1.id < t2.id |
| “Find gaps” | LEAD | LEAD(id) OVER (ORDER BY id) - id > 1 |
| “Every X” | COUNT DISTINCT + compare | HAVING COUNT(DISTINCT month) = total_months |
| “Pivot” | CASE WHEN in SUM | SUM(CASE WHEN col = 'val' THEN amt END) |
| “Complex multi-step” | CTEs | WITH step1 AS (...), step2 AS (...) SELECT ... |
Final Tips for SQL Interviews
-
Clarify before coding — ask about edge cases (NULLs, ties, empty results) before writing the query. Interviewers WANT you to ask.
-
Start with the approach, not the syntax — say “I will use a window function with PARTITION BY department” before writing SQL. Show your thinking.
-
Write step by step — do not try to write the perfect query in one shot. Start with the inner query, verify it works, then build outward. Use CTEs to make this visible.
-
Handle NULLs explicitly — mention how your query handles NULLs. “If manager_id is NULL, this LEFT JOIN will include them with NULL manager columns.”
-
Consider performance — after solving, mention: “For a large table, I would add an index on department and salary.” Shows production awareness.
-
Know your ranking functions — ROW_NUMBER vs RANK vs DENSE_RANK is asked in 80% of SQL interviews. Know the difference cold.
-
Practice the self-join — “employees earning more than their manager” is the #1 SQL interview question across all companies. Practice until it is automatic.
-
Use CTEs over nested subqueries — CTEs are more readable, easier to debug live, and interviewers can follow your logic step by step.
-
Talk about alternatives — after solving with one approach, mention: “This can also be solved with a correlated subquery, but the window function approach is more efficient.” Shows depth.
-
Test with edge cases mentally — after writing, walk through: “What if there is only one employee in the department? What if all salaries are the same? What if the table is empty?”
Related posts: – SQL Execution Order & WHERE Clauses – SQL Joins – SQL Window Functions – Subqueries & Performance – CTEs & Subqueries – Top 20 Data Engineering Interview Questions
Naveen Vuppula is a Senior Data Engineering Consultant and app developer based in Ontario, Canada. He writes about Python, SQL, AWS, Azure, and everything data engineering at DriveDataScience.com.