CodeGym /Courses /SQL SELF /Using subqueries in FROM

Using subqueries in FROM

SQL SELF
Level 14 , Lesson 1
Available

Let’s go even deeper down the rabbit hole: let’s see how to use subqueries in the FROM clause. This is one of the most common tricks SQL devs use, because it lets you build powerful temporary tables right “on the spot” and reuse them as if they actually exist in your database.

Imagine you’re building a report that needs calculations, grouping, or filtering, but you don’t want to create extra temp tables on the server. What do you do? That’s where subqueries in FROM come to the rescue. They let you:

  • Temporarily join or aggregate data before your main query runs.
  • Create structured data sets “on the fly.”
  • Cut down on operations, asking the database to store as little intermediate data as possible.

Subqueries in FROM act like mini-tables you can use in your main query. It’s like building with Legos: fast, flexible, and without extra overhead :)

Subquery basics in FROM

With subqueries in FROM, we use a subquery to create a temporary table (or subtable) that becomes part of the overall query. Here’s what you need to do, step by step:

  1. Write your subquery inside the FROM clause in parentheses.
  2. Give the subquery an alias (nickname).
  3. Use that alias just like it’s a real table.

Syntax

SELECT columns
FROM (
    SELECT columns
    FROM table
    WHERE condition
) AS alias
WHERE outer_condition;

Sounds a bit scary? Let’s jump into some examples.

Example: Students and average grade

Let’s say we have two tables:

students (student data — their name and ID):

student_id student_name
1 Alex
2 Anna
3 Dan

grades (student grades data):

grade_id student_id grade
1 1 80
2 1 85
3 2 90
4 3 70
5 3 75

Now the task: get a list of students and their average grade.

We can start with a simple subquery that calculates each student’s average grade, then use it in the main query.

SELECT s.student_name, g.avg_grade
FROM (
    SELECT student_id, AVG(grade) AS avg_grade
    FROM grades
    GROUP BY student_id
) AS g
JOIN students AS s ON s.student_id = g.student_id;

Result:

student_name avg_grade
Alex 82.5
Anna 90.0
Dan 72.5

On-the-fly temporary tables

Subqueries in FROM are especially handy when you need more than one level of data processing. For example, if you want not just the average grade, but also the max grade for each student — all in one query.

SELECT g.student_id, g.avg_grade, g.max_grade
FROM (
    SELECT student_id, 
           AVG(grade) AS avg_grade, 
           MAX(grade) AS max_grade
    FROM grades
    GROUP BY student_id
) AS g;

Result:

student_id avg_grade max_grade
1 82.5 85
2 90 90
3 72.5 75

Notice how this works just like a real temporary table with its own columns: avg_grade and max_grade.

When are subqueries in FROM best to use?

For aggregated data. If you want to do calculations first (like averages, sums, or maxes), then join the results with other tables.

For filtering data. When you need to filter data before joining it with the main table.

For simplifying complex queries. Breaking up tough tasks into steps helps you avoid confusion.

Example: Student report with two levels of processing

Let’s say we want to find students whose average grade is above 80. We’ll start with a subquery that calculates averages, then use it in a filter.

SELECT s.student_name, g.avg_grade
FROM students AS s
JOIN (
    SELECT student_id, AVG(grade) AS avg_grade
    FROM grades
    GROUP BY student_id
) AS g ON s.student_id = g.student_id
WHERE g.avg_grade > 80;

Result:

student_name avg_grade
Alex 82.5
Anna 90.0

Usage tips and recommendations

Alias is required. Always give your subquery an alias (like AS g), otherwise PostgreSQL won’t know how to refer to this “temporary table.”

Optimization. Subqueries in FROM can be slower than table JOINs, especially if you’re filtering data inside the subquery.

Indexing. Make sure the fields you use for joining, indexes, and filters are optimized — it really affects performance.

Example of a complex query: courses and student count

Now let’s do a real-world task that’s a bit tougher. Imagine we have this table:

courses (list of courses):

course_id course_name
1 SQL Basics
2 Python Basics

And enrollments (student enrollments in courses):

student_id course_id
1 1
1 2
2 1

Now we want to know how many students are enrolled in each course.

SELECT c.course_name, e.students_count
FROM courses AS c
JOIN (
    SELECT course_id, COUNT(student_id) AS students_count
    FROM enrollments
    GROUP BY course_id
) AS e ON c.course_id = e.course_id;

Result:

course_name students_count
SQL Basics 2
Python Basics 1

Hope you liked the lecture... but the next one will be even cooler :)

2
Task
SQL SELF, level 14, lesson 1
Locked
Counting the Number of Students with Grades
Counting the Number of Students with Grades
2
Task
SQL SELF, level 14, lesson 1
Locked
Number of students in each course
Number of students in each course
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION