Practice SQL Queries (by Interview Pattern)
Practice organized by syntax wastes time. You drill JOINs, then you drill GROUP BY, then you drill window functions, and on interview day the question composes 3 of them in a way none of the drills did. The 854 queries here are filed by the shape of the answer, not the keyword used to write it. 8 shapes cover roughly 90 percent of interview SQL.
Practice organized by syntax wastes time. You drill JOINs, then you drill GROUP BY, then you drill window functions, and on interview day the question composes 3 of them in a way none of the drills did. The 854 queries here are filed by the shape of the answer, not the keyword used to write it. 8 shapes cover roughly 90 percent of interview SQL.
Know the patterns before the interviewer asks them.
Pattern frequency in interview SQL
Share of SQL interview questions per pattern, drawn from the catalog's source write-ups.
8 patterns with their skeleton query
Each pattern has a recognizable shape, a set of keywords that show up together, and a typical failure mode that costs candidates the round.
WITH ranked AS (
SELECT *,
ROW_NUMBER() OVER (
PARTITION BY group_col
ORDER BY rank_col DESC, tiebreak_col
) AS rn
FROM source
)
SELECT * FROM ranked WHERE rn <= N;WITH ranked AS (
SELECT *,
ROW_NUMBER() OVER (
PARTITION BY natural_key
ORDER BY updated_at DESC, id DESC
) AS rn
FROM source
)
SELECT * FROM ranked WHERE rn = 1;WITH gapped AS (
SELECT user_id, event_at,
CASE WHEN event_at - LAG(event_at) OVER (
PARTITION BY user_id ORDER BY event_at) > INTERVAL '30 minutes'
THEN 1 ELSE 0 END AS new_session
FROM events
),
sessioned AS (
SELECT *, SUM(new_session) OVER (
PARTITION BY user_id ORDER BY event_at) AS session_id
FROM gapped
)
SELECT user_id, session_id, MIN(event_at), MAX(event_at)
FROM sessioned
GROUP BY user_id, session_id;SELECT user_id,
MIN(activity_date) AS streak_start,
MAX(activity_date) AS streak_end,
COUNT(*) AS streak_length
FROM (
SELECT user_id, activity_date,
activity_date - (ROW_NUMBER() OVER (
PARTITION BY user_id ORDER BY activity_date
)) * INTERVAL '1 day' AS streak_key
FROM user_activity
) t
GROUP BY user_id, streak_key
ORDER BY user_id, streak_start;SELECT f.fact_id, f.event_time, d.attribute_value FROM fact_table f LEFT JOIN dim_scd2 d ON d.entity_id = f.entity_id AND d.effective_from <= f.event_time AND (d.effective_to IS NULL OR f.event_time < d.effective_to);
SELECT signup_month, COUNT(*) FILTER (WHERE status = 'active') AS active, COUNT(*) FILTER (WHERE status = 'churned') AS churned, 100.0 * COUNT(*) FILTER (WHERE status = 'active') / COUNT(*) AS active_pct FROM users GROUP BY signup_month;
SELECT
account_id, posted_at, amount,
SUM(amount) OVER (
PARTITION BY account_id
ORDER BY posted_at
ROWS UNBOUNDED PRECEDING
) AS running_balance,
AVG(amount) OVER (
PARTITION BY account_id
ORDER BY posted_at
ROWS BETWEEN 6 PRECEDING AND CURRENT ROW
) AS rolling_7
FROM transactions;MERGE INTO daily_revenue d
USING (
SELECT
DATE(event_at) AS revenue_date,
SUM(amount) AS revenue
FROM events
WHERE processed_at >= :since
GROUP BY DATE(event_at)
) src
ON d.revenue_date = src.revenue_date
WHEN MATCHED THEN UPDATE SET revenue = d.revenue + src.revenue
WHEN NOT MATCHED THEN INSERT (revenue_date, revenue)
VALUES (src.revenue_date, src.revenue);How many queries to solve, by target level
Queries to solve and the patterns to focus on. Higher levels add patterns rather than increasing depth on existing ones.
| Target | Queries to solve | Patterns to cover | Secondary focus |
|---|---|---|---|
| Phone screen (Easy/Medium) | 20-30 | Top-N, dedup, pivot | GROUP BY + window function fluency |
| Mid-level onsite | 60-80 | Add running windows, gap-and-island | Multi-CTE composition, frame clauses |
| Senior onsite | 120-150 | Add SCD, sessionization, late-arriving | Tradeoff articulation, NULL semantics |
| FAANG / staff | 180-220 | All 8 patterns, recursive CTEs | Pattern combination, optimization reasoning |
3 practice plans that don't work
Common ways candidates burn time without building interview-relevant skill.
Drilling 50 GROUP BY problems first
Aggregation is the most common keyword in interview SQL but it's almost never the only one. Most aggregation questions are conditional aggregation pivots that also test CASE WHEN, NULL handling, and percent-of-total. Drill the pivot pattern, not GROUP BY.
Memorizing 'find the Nth highest salary' answers
There are at least 3 correct answers depending on what 'highest' means. The pattern is DENSE_RANK = N for the Nth distinct value, RANK = N for the Nth position with gaps, ROW_NUMBER = N if you want exactly 1 row. Picking which interpretation the question wants is the actual skill.
Studying by SQL keyword alphabetically
A practice plan that goes 'today: JOINs, tomorrow: GROUP BY, next week: window functions' wastes the highest-leverage time. The shape-based plan above hits the high-percentage patterns first and lets the keywords come along for the ride.
Pattern-based SQL practice FAQ
Why organize practice by pattern instead of by SQL keyword?+
How many SQL queries should I practice before an interview?+
What's the highest-frequency SQL interview question shape?+
How do I know which pattern a question is asking for?+
Do these patterns transfer to BigQuery, Snowflake, and Redshift?+
Pick a pattern and write a query
- 01
Active recall beats re-reading by 50%
Cognitive-science meta-reviews (Dunlosky et al., 2013) rank practice testing as a top-tier study technique, while re-reading and highlighting rank near the bottom
- 02
76% of hiring managers reject on the coding task, not the resume
From HackerRank's 2024 Developer Skills Report. Candidates who look strong on paper still fail the live screen if they haven't done timed, executable practice
- 03
Five problem shapes cover 80% of data engineer loops
Dedup, sessionization, top-N-per-group, slowly-changing dimensions, partition tricks. Writing the shapes by hand turns the unfamiliar into pattern recognition