LeetCode SQL 50 is LeetCode's official SQL study plan: 50 database problems in seven sections, running from basic SELECT filters through joins, aggregation, subqueries and string functions. The mix is 32 Easy, 17 Medium and 1 Hard. LeetCode describes it as basic to intermediate and pitches it as a one-month plan, but at about four problems a day you can finish it in two weeks. Most of the value sits in the 18 Medium and Hard problems, which cover the patterns most worth knowing cold for a SQL round: conditional aggregation, first-event lookups, running totals and top-N per group.
Every problem in the plan accepts MySQL, MS SQL Server, PostgreSQL and Oracle (plus Pandas), and at the time of writing all 50 are free to solve, so you do not need Premium.
What is in the SQL 50 study plan
The plan has seven sections, in this order on leetcode.com:
| Section | Problems | Difficulty | Core concepts |
|---|---|---|---|
| Select | 5 | 5 Easy | WHERE, AND/OR, NULL checks, DISTINCT, string length |
| Basic Joins | 9 | 7 Easy, 2 Medium | INNER, LEFT and CROSS joins, self-joins, anti-joins, HAVING |
| Basic Aggregate Functions | 8 | 5 Easy, 3 Medium | AVG, ROUND, weighted averages, percentages, conditional sums |
| Sorting and Grouping | 7 | 5 Easy, 2 Medium | COUNT(DISTINCT), date ranges, HAVING, relational division |
| Advanced Select and Joins | 7 | 3 Easy, 4 Medium | CASE, UNION, consecutive rows, as-of-date lookups, running totals |
| Subqueries | 7 | 1 Easy, 5 Medium, 1 Hard | NOT IN, correlated subqueries, moving averages, top-N per group |
| Advanced String Functions / Regex / Clause | 7 | 6 Easy, 1 Medium | UPPER/LOWER/SUBSTRING, LIKE, REGEXP, string aggregation, DELETE |
The difficulty curve is not smooth. The first four sections are mostly Easy, while Advanced Select and Joins and Subqueries hold 9 of the 17 Mediums plus the only Hard. If you run short on time, it will be in the part of the plan that matters most.
What each section actually tests
Select (5). Plain filtering, with one lesson worth slowing down for. In Find Customer Referee, the answer must include customers with no referee at all, and referee_id != 2 silently drops them because their referee_id is NULL. Invalid Tweets is a reminder that in MySQL LENGTH counts bytes while CHAR_LENGTH counts characters.
Basic Joins (9). The core join vocabulary: an INNER JOIN (Product Sales Analysis I), LEFT JOINs that keep unmatched rows (Replace Employee ID With The Unique Identifier, Employee Bonus), the anti-join, where you LEFT JOIN and keep rows whose right side IS NULL (Customer Who Visited but Did Not Make Any Transactions), a self-join on dates (Rising Temperature) and a CROSS JOIN that builds every student and subject pair before counting (Students and Examinations). Confirmation Rate combines a LEFT JOIN with a conditional average and a default of 0.
Basic Aggregate Functions (8). AVG and ROUND first, then harder shapes: a weighted average joined on a date range (Average Selling Price, which is total revenue over total units, with 0 for products that never sold), percentages with a scalar-subquery denominator (Percentage of Users Attended a Contest), conditional sums by month (Monthly Transactions I) and first-event analysis (Immediate Food Delivery II, Game Play Analysis IV).
Sorting and Grouping (7). GROUP BY with COUNT(DISTINCT) and HAVING. Product Sales Analysis III asks for every sale in each product's first year, not just one row. Customers Who Bought All Products is relational division: keep customers whose COUNT(DISTINCT product_key) equals the total number of products. Biggest Single Number must return NULL when no number qualifies.
Advanced Select and Joins (7). CASE expressions (Triangle Judgement), a UNION of two rules (Primary Department for Each Employee), rows-in-sequence logic (Consecutive Numbers), an as-of-date lookup with a default price of 10 (Product Price at a Given Date), a running total against a 1,000 kg limit (Last Person to Fit in the Bus) and Count Salary Categories, which must output all three categories even when one has zero accounts.
Subqueries (7). The hardest section. NOT IN against the list of current employees (Employees Whose Manager Left the Company), swapping adjacent seats while leaving a final odd one alone (Exchange Seats), two independent ranked answers joined with UNION ALL (Movie Rating), a seven-day moving average (Restaurant Growth), stacking two columns into one before counting (Friend Requests II: Who Has the Most Friends), multi-condition filtering with counts (Investments in 2016) and the plan's only Hard, Department Top Three Salaries.
Advanced String Functions / Regex / Clause (7). Mostly Easy, but full of less-common syntax: capitalizing names with UPPER, LOWER and SUBSTRING (Fix Names in a Table), prefix matching with LIKE (Patients With a Condition), a DELETE that keeps the smallest id (Delete Duplicate Emails), string aggregation with GROUP_CONCAT or STRING_AGG (Group Sold Products By The Date), regex validation (Find Users With Valid E-Mails) and the well-known Second Highest Salary.
Where window functions show up
SQL 50 has no section called window functions. They fit naturally into a handful of problems spread across Basic Aggregate Functions, Advanced Select and Joins and Subqueries, and almost every one also has a join or subquery solution. Learn both: the second version proves you understand what the window is doing and gives you a fallback if a follow-up rules windows out.
| Problem | With a window function | Without one |
|---|---|---|
| Consecutive Numbers | LAG and LEAD ordered by id | Join the table to itself on id + 1 and id + 2 |
| Last Person to Fit in the Bus | SUM(weight) OVER (ORDER BY turn) | Self-join summing weights where turn ≤ the current turn |
| Restaurant Growth | Daily totals, then a sum over ROWS BETWEEN 6 PRECEDING AND CURRENT ROW | Self-join on a seven-day date range |
| Exchange Seats | LAG/LEAD by id | CASE on id parity, with a check for the last odd id |
| Immediate Food Delivery II | ROW_NUMBER per customer by order date | MIN(order_date) per customer in a subquery |
| Department Top Three Salaries | DENSE_RANK per department | Correlated subquery counting distinct higher salaries |
The Hard problem is a good template for top-N-per-group questions in general:
SELECT d.name AS Department, e.name AS Employee, e.salary AS Salary
FROM (
SELECT name, salary, departmentId,
DENSE_RANK() OVER (PARTITION BY departmentId
ORDER BY salary DESC) AS rnk
FROM Employee
) e
JOIN Department d ON d.id = e.departmentId
WHERE e.rnk <= 3;
It uses DENSE_RANK, not RANK or ROW_NUMBER, because the problem asks for the top three unique salaries: two people tied at the top both count, and the next salary is still rank 2. In Restaurant Growth, a ROWS frame of six preceding rows only works because the problem guarantees at least one customer every day, so after you total by day, each row is exactly one calendar day.
A two-week order
This keeps LeetCode's section order with one change: the string section moves up to day 8. It is mostly Easy, so it gives you a lighter day before the two hardest sections.
| Day | Problems | Focus |
|---|---|---|
| 1 | Select (all 5) | WHERE, NULL checks |
| 2 | Basic Joins 1–5 | INNER vs LEFT, anti-join, date self-join |
| 3 | Basic Joins 6–9 | NULL-safe filters, CROSS JOIN, HAVING, conditional average |
| 4 | Basic Aggregate Functions 1–5 | AVG, ROUND, weighted average, percentages |
| 5 | Basic Aggregate Functions 6–8 | Conditional sums, first-event analysis (all Medium) |
| 6 | Sorting and Grouping 1–4 | COUNT(DISTINCT), date ranges, first-year lookup |
| 7 | Sorting and Grouping 5–7, then re-solve misses | NULL results, relational division |
| 8 | Advanced String Functions / Regex / Clause (all 7) | String functions, LIKE, regex, DELETE |
| 9 | Advanced Select and Joins 1–4 | Self-joins, UNION, CASE, consecutive rows |
| 10 | Advanced Select and Joins 5–7 | As-of-date lookups, running totals, zero-count rows |
| 11 | Subqueries 1–4 | NOT IN, seat swaps, UNION ALL, moving average |
| 12 | Subqueries 5–7 | Stacking columns, multi-condition filters, DENSE_RANK |
| 13 | Re-solve every Medium and the Hard from a blank editor | Recall and second solutions |
| 14 | Timed mixed set, explained out loud | Interview conditions |
If you have a full month, keep the same order at about two new problems a day and spend the extra time writing a second solution for every Medium. Budget roughly 15 minutes for an Easy and 30 for a Medium; past that, read the editorial, then re-solve the problem from scratch the next day.
The traps that fail correct-looking queries
Most failed submissions here come from edge cases the example input does not show.
- NULL is not “not equal.” Any comparison with NULL returns NULL, not true, so
referee_id != 2skips customers with no referee. AddOR referee_id IS NULLor useCOALESCE. Employee Bonus has the same trap. COUNT(*)after a LEFT JOIN.COUNT(*)counts rows, including the NULL-padded row for a missing match, so a student who skipped an exam shows 1 instead of 0. Count a column from the right-hand table instead, sinceCOUNT(column)ignores NULLs.GROUP BYcannot invent empty groups. In Count Salary Categories, a category with no accounts has no rows to group. Build the three categories yourself with aUNION ALLof three counts, or a small derived table joined to the data.- Empty result vs. NULL. Second Highest Salary and Biggest Single Number expect a NULL when nothing qualifies, not an empty table. Wrap the query in a scalar subquery, or use
MAX, which returns NULL on empty input. - Integer division. In PostgreSQL and SQL Server, dividing two integers truncates, so a one-in-three rate becomes 0. Multiply by
1.0or cast before dividing. In PostgreSQL, two-argumentROUNDneeds a numeric value, so cast floating-point results first. MySQL's/keeps the fractional part even for two integers, which is why a query can pass in MySQL and fail in PostgreSQL. - Aggregate before you window. Restaurant Growth can have several customers on the same day. Total the amounts per day first, then average over days, and return only days with a full seven-day window: the expected output starts on the seventh day.
- Read “distinct” and “unique” literally. “Second highest distinct salary” and “top three unique salaries” both mean ties collapse into one value. Use
DISTINCTorDENSE_RANK, notROW_NUMBERor a bareLIMIT. - Compare dates, not ids. In Rising Temperature, “yesterday” means the previous calendar date. Join on date arithmetic such as
DATEDIFF, since nothing in the problem says ids follow date order. - Anchor your patterns. In Patients With a Condition, a code starting with DIAB1 can sit at the start of the string or after a space, so check both. In Find Users With Valid E-Mails, anchor the regex with
^and$and escape the dot in leetcode.com. The problem requires a lowercase domain, but MySQL regex matching follows the column's collation and is often case-insensitive, so force case-sensitive matching withREGEXP_LIKE(mail, pattern, 'c').
How to work each problem so it transfers
- Read the schema notes first. Primary keys and lines like “this table may contain duplicates” tell you whether you need
DISTINCTbefore you write anything. - Write the expected output by hand. Know exactly which rows should survive each step.
- Build in evaluation order.
FROMand joins, thenWHERE,GROUP BY,HAVING,SELECTand finallyORDER BY. Run the query after each step and check the row count. Use CTEs (WITHclauses) for multi-step problems; they are easier to explain. - Write a second solution for every Medium. Window function vs. join, or subquery vs. CTE.
- Explain it out loud. When a SQL round is live, the interviewer hears your reasoning, not just your result. To rehearse that part, run a free AI mock interview in CoPilot Interview with a technical question mix: the AI interviewer reads each question aloud, you answer out loud, and every answer gets a score with what worked and what to improve.
If your SQL screen is an online assessment, complete it on your own under the employer's rules. Our new grad online assessment guide covers how those tests usually work.
Is SQL 50 enough for a SQL interview?
It depends on how central SQL is to the role.
- Software engineering roles where SQL is one topic among many. Finishing SQL 50 with re-solves is a reasonable bar. Your time is better spent on algorithm practice, such as the LeetCode 75 study plan or Top Interview 150.
- Data analyst, data engineer and data science roles. Treat SQL 50 as the floor. It has a single Hard problem and only a handful of window-function problems. Add more from LeetCode's Database problem set, focusing on ranking,
LAG/LEAD, running totals, date bucketing and multi-step CTEs. LeetCode's follow-on plan, Advanced SQL 50, is Premium-only at the time of writing; our take on whether LeetCode Premium is worth it can help you decide. - New-grad loops. Pair the plan with our entry-level software engineer interview questions, since SQL may be one part of a longer round.
If the posting names a database, practice in that dialect. The concepts transfer, but integer division, string aggregation (GROUP_CONCAT in MySQL, STRING_AGG in PostgreSQL and SQL Server) and date functions do not.
Practice explaining your queries out loud
CoPilot Interview's free AI mock interviews build questions from your résumé and the job description, let you pick a technical mix, and score each spoken answer with what worked and what to improve. The free plan gives you 15 minutes a day, with no credit card.
Try a free AI mock interviewFAQ
What is LeetCode SQL 50?
SQL 50 is LeetCode's official SQL study plan: 50 database problems in seven sections, from basic SELECT filters through joins, aggregation, subqueries and string functions. It contains 32 Easy, 17 Medium and 1 Hard problem.
Is LeetCode SQL 50 free?
Yes. At the time of writing, all 50 problems in the plan are free to solve, so you do not need LeetCode Premium to finish it.
How long does LeetCode SQL 50 take?
LeetCode pitches it as a one-month plan. At about four new problems a day you can finish in under two weeks, leaving time to re-solve the Medium problems and do a timed review.
Which SQL dialect should I use for SQL 50?
Use the dialect your target job uses, or MySQL if you do not know. The problems accept MySQL, MS SQL Server, PostgreSQL and Oracle (plus Pandas), and details such as integer division and string aggregation differ between them.
Does SQL 50 cover window functions?
Yes, but not in a dedicated section. Problems such as Consecutive Numbers, Last Person to Fit in the Bus, Restaurant Growth and Department Top Three Salaries are natural fits for LAG, running sums, moving-window sums and DENSE_RANK.