Home › Blog › LeetCode SQL 50 Guide

LeetCode SQL 50: Study Plan Guide for SQL Interviews

LeetCode's free 50-problem SQL study plan, broken down by section: the concepts each one tests, where window functions show up, a two-week order and the traps that cost submissions.

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:

SectionProblemsDifficultyCore concepts
Select55 EasyWHERE, AND/OR, NULL checks, DISTINCT, string length
Basic Joins97 Easy, 2 MediumINNER, LEFT and CROSS joins, self-joins, anti-joins, HAVING
Basic Aggregate Functions85 Easy, 3 MediumAVG, ROUND, weighted averages, percentages, conditional sums
Sorting and Grouping75 Easy, 2 MediumCOUNT(DISTINCT), date ranges, HAVING, relational division
Advanced Select and Joins73 Easy, 4 MediumCASE, UNION, consecutive rows, as-of-date lookups, running totals
Subqueries71 Easy, 5 Medium, 1 HardNOT IN, correlated subqueries, moving averages, top-N per group
Advanced String Functions / Regex / Clause76 Easy, 1 MediumUPPER/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.

ProblemWith a window functionWithout one
Consecutive NumbersLAG and LEAD ordered by idJoin the table to itself on id + 1 and id + 2
Last Person to Fit in the BusSUM(weight) OVER (ORDER BY turn)Self-join summing weights where turn ≤ the current turn
Restaurant GrowthDaily totals, then a sum over ROWS BETWEEN 6 PRECEDING AND CURRENT ROWSelf-join on a seven-day date range
Exchange SeatsLAG/LEAD by idCASE on id parity, with a check for the last odd id
Immediate Food Delivery IIROW_NUMBER per customer by order dateMIN(order_date) per customer in a subquery
Department Top Three SalariesDENSE_RANK per departmentCorrelated 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.

DayProblemsFocus
1Select (all 5)WHERE, NULL checks
2Basic Joins 1–5INNER vs LEFT, anti-join, date self-join
3Basic Joins 6–9NULL-safe filters, CROSS JOIN, HAVING, conditional average
4Basic Aggregate Functions 1–5AVG, ROUND, weighted average, percentages
5Basic Aggregate Functions 6–8Conditional sums, first-event analysis (all Medium)
6Sorting and Grouping 1–4COUNT(DISTINCT), date ranges, first-year lookup
7Sorting and Grouping 5–7, then re-solve missesNULL results, relational division
8Advanced String Functions / Regex / Clause (all 7)String functions, LIKE, regex, DELETE
9Advanced Select and Joins 1–4Self-joins, UNION, CASE, consecutive rows
10Advanced Select and Joins 5–7As-of-date lookups, running totals, zero-count rows
11Subqueries 1–4NOT IN, seat swaps, UNION ALL, moving average
12Subqueries 5–7Stacking columns, multi-condition filters, DENSE_RANK
13Re-solve every Medium and the Hard from a blank editorRecall and second solutions
14Timed mixed set, explained out loudInterview 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.

  1. NULL is not “not equal.” Any comparison with NULL returns NULL, not true, so referee_id != 2 skips customers with no referee. Add OR referee_id IS NULL or use COALESCE. Employee Bonus has the same trap.
  2. 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, since COUNT(column) ignores NULLs.
  3. GROUP BY cannot invent empty groups. In Count Salary Categories, a category with no accounts has no rows to group. Build the three categories yourself with a UNION ALL of three counts, or a small derived table joined to the data.
  4. 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.
  5. Integer division. In PostgreSQL and SQL Server, dividing two integers truncates, so a one-in-three rate becomes 0. Multiply by 1.0 or cast before dividing. In PostgreSQL, two-argument ROUND needs 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.
  6. 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.
  7. Read “distinct” and “unique” literally. “Second highest distinct salary” and “top three unique salaries” both mean ties collapse into one value. Use DISTINCT or DENSE_RANK, not ROW_NUMBER or a bare LIMIT.
  8. 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.
  9. 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 with REGEXP_LIKE(mail, pattern, 'c').

How to work each problem so it transfers

  1. Read the schema notes first. Primary keys and lines like “this table may contain duplicates” tell you whether you need DISTINCT before you write anything.
  2. Write the expected output by hand. Know exactly which rows should survive each step.
  3. Build in evaluation order. FROM and joins, then WHERE, GROUP BY, HAVING, SELECT and finally ORDER BY. Run the query after each step and check the row count. Use CTEs (WITH clauses) for multi-step problems; they are easier to explain.
  4. Write a second solution for every Medium. Window function vs. join, or subquery vs. CTE.
  5. 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.

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 interview

FAQ

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.