SQL Subquery Practice — 57 Challenges (42 Free)

57 challenges whose reference solution uses a subquery — a SELECT in parentheses that is not a CTE — counted from the live bank: 8 Easy, 32 Medium, 17 Hard. 42 are free to play, including 2 free Hard previews; the rest are Pro. If the shape is new, take the on-ramp in order: A Subquery That Returns One Value, A Subquery That Returns a Set (IN), then Filter on an Average You Just Computed, which is a whole query sitting in the FROM clause. After that the Medium ladder is wide. Real datasets, AI tutoring.

Start Practicing Free →

Prefer to name the step instead of nesting it? CTE vs subquery vs temp table settles when the choice matters, and the CTE challenges are the WITH-clause side of the same bank.

A subquery as a value or a list — scalar and IN: 36 challenges (25 free)

WHERE salary > (SELECT AVG(salary) FROM employees) — the inner query runs once and returns one value; WHERE customer_id IN (SELECT …) returns a list to match against. The Easy on-ramps are here: A Subquery That Returns One Value, then the same shape on a second schema in Movies Rated Above the Average; then A Subquery That Returns a Set (IN), and Every Film by a Director Who Once Hit 8.5, where the list comes out of the table you are already selecting from. The Medium ones move the scalar into the SELECT list, into HAVING, inside COALESCE; the Hard ones nest it inside a CTE or divide by it for a share. Mind NOT IN against a nullable column — the NOT IN trap.

Counted from the challenge bank, September 2026. Easy first, then Medium, then Hard.

Easy Free

A Subquery That Returns One Value

Uses: Subquery WHERE

Try it →
Easy Free

Movies Rated Above the Average

Uses: Subquery WHERE

Try it →
Easy Free

A Subquery That Returns a Set (IN)

Uses: Subquery IN WHERE

Try it →
Easy Free

Every Film by a Director Who Once Hit 8.5

Uses: Subquery IN WHERE

Try it →
Easy Free

Transaction Share by Merchant Category

Uses: Subquery JOIN GROUP BY

Try it →
Easy Free

Better Than Its Own Genre

Uses: Subquery Correlated Subquery WHERE

Try it →
Medium Free

Handle NULL Ages

Uses: NULL Handling COALESCE Aggregation GROUP BY

Try it →
Medium Free

Simple Subquery: Above Average

Uses: Subquery WHERE

Try it →
Medium Free

Subquery in FROM (Derived Table)

Uses: Subquery Derived Table JOIN GROUP BY

Try it →
Medium Free

Consistent Director Analysis

Uses: GROUP BY HAVING Aggregation Subquery

Try it →
Medium Free

Below Department Average

Uses: Subquery WHERE Aggregation

Try it →
Medium Free

Highest Total Salary Budget Department

Uses: GROUP BY Aggregation Subquery HAVING

Try it →
Medium Free

Fare Imputation Analysis

Uses: NULL Handling COALESCE GROUP BY Aggregation

Try it →
Medium Free

Genres Without Blockbusters

Uses: Subquery NOT IN DISTINCT NOT IN / Subquery

Try it →
Medium Free

Highest Rated by Genre

Uses: Subqueries Aggregation WHERE GROUP BY

Try it →
Medium Free

Director Consistency Report

Uses: GROUP BY HAVING Aggregation Subquery

Try it →
Medium Free

Above-Department-Average Earners

Uses: Subquery Correlated Subquery

Try it →
Medium Free

Delete Duplicate Records, Keep the Original

Uses: DELETE DML Subquery Aggregation

Try it →
Medium Free

Recompute a Stale Counter

Uses: UPDATE DML Correlated Subquery Aggregation

Try it →
Medium Free

Banks Above National Average Assets

Finance & Banking track · Uses: WHERE Subquery

Try it →
Medium Free

Above-Median NPL Banks

Finance & Banking track · Uses: CTE JOIN Subquery

Try it →
Medium Free

Above-Average-Assess Properties

Real Estate track · Uses: WHERE Subquery

Try it →
Medium Free

Above-Average Tool Wear

Manufacturing & Industry track · Uses: WHERE Subquery

Try it →
Medium Free

ROA Outliers — Top 5% by Return

Finance & Banking track · Uses: Subquery JOIN ORDER BY

Try it →
Medium Free

Dormant Cards — No Transactions in the Last 14 Days

Uses: LEFT JOIN IS NULL CTE

Try it →
Hard Pro

Correlated Subquery: Employees Above Department Median

Uses: Correlated Subquery COUNT WHERE

Try it →
Hard Pro

Wealthy Survivor Profile

Uses: WHERE Subquery ORDER BY

Try it →
Hard Pro

Top Spender Per Country

Uses: Subquery GROUP BY HAVING Aggregation

Try it →
Hard Pro

Highest Fare Per Port

Uses: Subquery WHERE Aggregation ORDER BY

Try it →
Hard Pro

Order Status Dashboard

Uses: GROUP BY Aggregation Subquery ORDER BY

Try it →
Hard Pro

Anti-Join Pipeline: Unmatched Records

Uses: LEFT JOIN IS NULL CTE GROUP BY

Try it →
Hard Pro

Department Budget Analysis

Uses: CTE Aggregation GROUP BY Subquery

Try it →
Hard Pro

Customers with Orders in ALL Categories

Uses: GROUP BY HAVING COUNT DISTINCT

Try it →
Hard Pro

QoQ Asset Growth — Top Banks

Finance & Banking track · Uses: Window Functions LAG JOIN

Try it →
Hard Pro

Banks with NPL Above 2-Sigma

Finance & Banking track · Uses: CTE Aggregation Subquery Statistics

Try it →
Hard Pro

How Concentrated Is the Spend?

Uses: Subquery CTE ROW_NUMBER CASE

Try it →

Correlated subqueries and EXISTS: 11 challenges (9 free)

The inner query references the outer row — WHERE e1.salary > (SELECT AVG(e2.salary) FROM employees e2 WHERE e2.department = e1.department) — so it runs once per row. EXISTS is the correlated form for existence checks, and NOT EXISTS is the anti-join that survives NULLs where NOT IN does not. Start on the two Easy ones: Better Than Its Own Genre, where a single m2.genre = m.genre turns one average into one average per row, and Who Manages Nobody, where the NOT IN version of the same question really does return zero rows. Then Departments With High Earners and EXISTS vs IN; Correlated Subquery: Employees Above Department Median is the Hard one that names the pattern. The anti-join explained has the NOT EXISTS walkthrough.

Counted from the challenge bank, September 2026. Easy first, then Medium, then Hard.

Easy Free

Better Than Its Own Genre

Uses: Subquery Correlated Subquery WHERE

Try it →
Easy Free

Who Manages Nobody

Uses: Subquery EXISTS Correlated Subquery WHERE

Try it →
Medium Free

EXISTS vs IN: Departments with Top Performers

Uses: EXISTS Subquery Correlated Subquery GROUP BY

Try it →
Medium Free

Management Hierarchy Overview

Uses: LEFT JOIN EXISTS Subquery COALESCE

Try it →
Medium Free

Departments With High Earners

Uses: EXISTS Subquery DISTINCT EXISTS Subquery

Try it →
Medium Free

Above-Department-Average Earners

Uses: Subquery Correlated Subquery

Try it →
Medium Free

Customers Without Orders

Uses: Subquery NOT EXISTS

Try it →
Medium Free

Recompute a Stale Counter

Uses: UPDATE DML Correlated Subquery Aggregation

Try it →
Medium Free

Cardholders Who Have Never Disputed a Charge

Uses: Subquery JOIN GROUP BY

Try it →
Hard Pro

Correlated Subquery: Employees Above Department Median

Uses: Correlated Subquery COUNT WHERE

Try it →
Hard Pro

Highest Fare Per Port

Uses: Subquery WHERE Aggregation ORDER BY

Try it →

Derived tables — a subquery in FROM: 18 challenges (13 free)

Aggregate first, then query the result — FROM (SELECT department, AVG(salary) AS avg_salary FROM employees GROUP BY department) d. Start with Filter on an Average You Just Computed, the Easy one — a country average computed in FROM and then filtered with a plain WHERE — then Subquery in FROM (Derived Table) and Above-Average Departments. The Hard ones wrap a window function you cannot filter in the same SELECT — ROW_NUMBER, LAG, a running SUM — and Running Total Revenue and Year-over-Year Growth are the 2 free Hard previews. Three sector ones wrap a UNION ALL. A derived table is the case where a CTE reads better; the CTE tutorial shows the rewrite.

Counted from the challenge bank, September 2026. Easy first, then Medium, then Hard — previews before Pro.

Easy Free

Filter on an Average You Just Computed

Uses: Subquery Derived Table Aggregation GROUP BY

Try it →
Medium Free

Subquery in FROM (Derived Table)

Uses: Subquery Derived Table JOIN GROUP BY

Try it →
Medium Free

Above-Average Departments (Derived Table)

Uses: Subquery JOIN Aggregation GROUP BY

Try it →
Medium Free

Department Roster with GROUP_CONCAT

Uses: GROUP BY GROUP_CONCAT Aggregation Subquery

Try it →
Medium Free

Highest Total Salary Budget Department

Uses: GROUP BY Aggregation Subquery HAVING

Try it →
Medium Free

UNION ALL Dedup: Cross-Dataset Search

Uses: UNION ALL UNION CTE CASE

Try it →
Medium Free

Inactive Customers by Tier

Uses: LEFT JOIN CASE GROUP BY Derived Table

Try it →
Medium Free

Month-over-Month Customer Growth

Uses: GROUP BY Aggregation Window Functions LAG

Try it →
Medium Free

Active and Recently Failed

Finance & Banking track · Uses: UNION ALL WHERE Set Operations

Try it →
Medium Free

Combined Sales and Permits Activity

Real Estate track · Uses: UNION ALL Set Operations

Try it →
Medium Free

Failure Mode Catalog Join

Manufacturing & Industry track · Uses: JOIN UNION ALL Set Operations

Try it →
Hard Free preview

Running Total Revenue

Uses: Subquery Window Functions GROUP BY Aggregation

Try it →
Hard Free preview

Year-over-Year Growth

Uses: Subquery Window Functions GROUP BY Aggregation

Try it →
Hard Pro

Customer Lifetime Value Pipeline

Uses: JOIN Subquery Window Functions NTILE

Try it →
Hard Pro

Top Spender Per Country

Uses: Subquery GROUP BY HAVING Aggregation

Try it →
Hard Pro

Nth Highest Salary per Department

Uses: Subquery Window Functions WHERE Window Function

Try it →
Hard Pro

Top Earner Per Department

Uses: Window Functions Subquery

Try it →
Hard Pro

Earliest Movie per Genre

Uses: Window Functions ROW_NUMBER PARTITION BY Subquery

Try it →

The subquery is the inside-out spelling; the same problems written top-down with WITH are on the CTE page, and several Hard ones here open with WITH and appear on both. When the choice matters — and when it does not — is on CTE vs subquery vs temp table.