Quick Reference Print-friendly • Last-minute revision

SQL Cheatsheet 🗃️

Essential SQL syntax, JOINs, window functions, and common query patterns for quick revision before interviews.

🔗 JOINs

-- INNER JOIN: Only matching rows
SELECT * FROM A INNER JOIN B ON A.id = B.a_id;

-- LEFT JOIN: All from A + matching from B (NULL if no match)
SELECT * FROM A LEFT JOIN B ON A.id = B.a_id;

-- FULL OUTER JOIN: All rows from both
SELECT * FROM A FULL OUTER JOIN B ON A.id = B.a_id;

-- CROSS JOIN: Cartesian product (every combo)
SELECT * FROM A CROSS JOIN B;

-- SELF JOIN: Table joins itself
SELECT e.name, m.name as manager
FROM employees e LEFT JOIN employees m ON e.manager_id = m.id;

🪟 Window Functions

-- ROW_NUMBER: Unique sequential (1,2,3,4)
-- RANK: Ties get same, gaps after (1,1,3,4)
-- DENSE_RANK: Ties same, no gaps (1,1,2,3)

SELECT name, dept, salary,
  ROW_NUMBER() OVER (PARTITION BY dept ORDER BY salary DESC) as rn,
  RANK() OVER (ORDER BY salary DESC) as rnk,
  DENSE_RANK() OVER (ORDER BY salary DESC) as drnk,
  LAG(salary) OVER (ORDER BY hire_date) as prev_salary,
  LEAD(salary) OVER (ORDER BY hire_date) as next_salary,
  SUM(salary) OVER (PARTITION BY dept) as dept_total,
  AVG(salary) OVER (ORDER BY hire_date ROWS 3 PRECEDING) as moving_avg
FROM employees;

📝 CTEs & Subqueries

-- CTE (Common Table Expression)
WITH high_earners AS (
  SELECT * FROM employees WHERE salary > 100000
)
SELECT dept, COUNT(*) FROM high_earners GROUP BY dept;

-- Recursive CTE (hierarchies)
WITH RECURSIVE hierarchy AS (
  SELECT id, name, 1 as level FROM employees WHERE manager_id IS NULL
  UNION ALL
  SELECT e.id, e.name, h.level + 1
  FROM employees e JOIN hierarchy h ON e.manager_id = h.id
)
SELECT * FROM hierarchy;

-- Correlated Subquery
SELECT * FROM employees e
WHERE salary > (SELECT AVG(salary) FROM employees WHERE dept = e.dept);

🎯 Common Interview Patterns

-- 2nd highest salary
SELECT MAX(salary) FROM emp WHERE salary < (SELECT MAX(salary) FROM emp);

-- Duplicate rows
SELECT email, COUNT(*) FROM users GROUP BY email HAVING COUNT(*) > 1;

-- Running total
SELECT date, amount, SUM(amount) OVER (ORDER BY date) as running_total FROM orders;

-- Top N per group
SELECT * FROM (
  SELECT *, DENSE_RANK() OVER (PARTITION BY dept ORDER BY salary DESC) as rnk
  FROM employees
) t WHERE rnk <= 3;

-- Consecutive days (gaps & islands)
SELECT id, date, date - ROW_NUMBER() OVER (ORDER BY date) * INTERVAL '1 day' as grp
FROM logins;

-- PIVOT (rows to columns)
SELECT * FROM scores
PIVOT (AVG(score) FOR subject IN ('Math', 'Science', 'English'));