Bored Analyst
SQL

Common Interview Patterns

Pattern 1: Find Duplicates

SELECT email, COUNT(*) AS dup_count
FROM users
GROUP BY email
HAVING COUNT(*) > 1;
WITH dupes AS (
  SELECT *, ROW_NUMBER() OVER (PARTITION BY email ORDER BY user_id) AS rn
  FROM users
)
SELECT * FROM dupes WHERE rn > 1;

Pattern 2: Nth Highest Value

WITH ranked AS (
  SELECT salary, DENSE_RANK() OVER (ORDER BY salary DESC) AS rnk
  FROM employees
)
SELECT DISTINCT salary
FROM ranked
WHERE rnk = 2;

Pattern 3: Consecutive Days / Streaks

WITH distinct_logins AS (
  SELECT DISTINCT user_id, login_date FROM user_logins
),
grouped AS (
  SELECT
    user_id,
    login_date,
    login_date - (ROW_NUMBER() OVER (
      PARTITION BY user_id ORDER BY login_date
    ))::INT * INTERVAL '1 day' AS group_key
  FROM distinct_logins
)
SELECT
  user_id,
  MIN(login_date) AS streak_start,
  MAX(login_date) AS streak_end,
  COUNT(*) AS consecutive_days
FROM grouped
GROUP BY user_id, group_key
HAVING COUNT(*) >= 2;

Pattern 4: Pivot Rows to Columns

SELECT
  product,
  SUM(CASE WHEN quarter = 'Q1' THEN revenue ELSE 0 END) AS q1,
  SUM(CASE WHEN quarter = 'Q2' THEN revenue ELSE 0 END) AS q2,
  SUM(CASE WHEN quarter = 'Q3' THEN revenue ELSE 0 END) AS q3,
  SUM(CASE WHEN quarter = 'Q4' THEN revenue ELSE 0 END) AS q4
FROM sales_data
GROUP BY product;

Pattern 5: Month-over-Month Growth

WITH monthly AS (
  SELECT
    DATE_TRUNC('month', sale_date) AS month,
    SUM(amount) AS total
  FROM sales
  GROUP BY 1
)
SELECT
  month,
  total AS current_month,
  LAG(total) OVER (ORDER BY month) AS prev_month,
  ROUND(
    (total - LAG(total) OVER (ORDER BY month)) * 100.0
    / NULLIF(LAG(total) OVER (ORDER BY month), 0),
    2
  ) AS mom_growth_pct
FROM monthly;

Pattern 6: Cohort Retention

WITH cohorts AS (
  SELECT user_id, DATE_TRUNC('month', signup_date) AS cohort
  FROM users
),
activity AS (
  SELECT c.user_id, c.cohort, DATE_TRUNC('month', a.activity_date) AS active_month
  FROM cohorts c
  LEFT JOIN user_activity a ON c.user_id = a.user_id
),
cohort_sizes AS (
  SELECT cohort, COUNT(DISTINCT user_id) AS size
  FROM cohorts
  GROUP BY cohort
)
SELECT
  a.cohort,
  a.active_month,
  EXTRACT(MONTH FROM AGE(a.active_month, a.cohort)) AS months_since_join,
  COUNT(DISTINCT a.user_id) AS active,
  cs.size AS cohort_size,
  ROUND(COUNT(DISTINCT a.user_id) * 100.0 / cs.size, 2) AS retention_pct
FROM activity a
JOIN cohort_sizes cs ON a.cohort = cs.cohort
WHERE a.active_month IS NOT NULL
GROUP BY a.cohort, a.active_month, cs.size
ORDER BY a.cohort, a.active_month;

Pattern 7: Top-N per Group

WITH ranked AS (
  SELECT *,
    ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) AS rn
  FROM employees
)
SELECT * FROM ranked WHERE rn <= 3;
SELECT d.dept_name, e.*
FROM departments d
CROSS JOIN LATERAL (
  SELECT employee_name, salary
  FROM employees
  WHERE department_id = d.id
  ORDER BY salary DESC
  LIMIT 3
) e;

On this page