CodeGym /Courses /SQL SELF /Extra conditions in JOIN: ON ... AND...

Extra conditions in JOIN: ON ... AND ...

SQL SELF
Level 12 , Lesson 2
Available

You already know how to join tables using JOIN. But in real life, just matching by keys isn’t always enough. A lot of times, you need to join data only if it matches some extra criteria — like only active records, only data for the current year, or only completed orders.

And that’s where extending the ON clause with AND comes into play.

Extra conditions in JOIN ... ON let you precisely control which rows get joined, even before SQL starts building the result. This makes your query:

  • Faster (fewer rows go through the JOIN),
  • More accurate (filtering happens at the join stage),
  • More predictable when using LEFT JOIN (unlike filtering in WHERE).

Example: Only active course enrollments

Let’s say the enrollments table has a student’s participation status: active, dropped, pending.

students table:

id name
1 Otto Song
2 Maria Chi
3 Alex Lin

Updated enrollments table:

student_id course_id status
1 101 active
1 103 active
2 102 dropped
3 101 active

courses table:

id name
101 Mathematics
102 Physics
103 Computer Science

Now we want to get only those students who have active courses:

SELECT 
    students.name AS student_name,
    courses.name AS course_name
FROM students 
INNER JOIN enrollments 
    ON students.id = enrollments.student_id
    AND enrollments.status = 'active'
INNER JOIN courses 
    ON enrollments.course_id = courses.id;

Result:

student_name course_name
Otto Song Mathematics
Otto Song Computer Science
Alex Lin Mathematics

Here we added AND enrollments.status = 'active' inside the ON so the join happens only for active records, not filtered after joining.

Why not WHERE?

You could write it like this:

...
WHERE enrollments.status = 'active'

But this behaves differently with LEFT JOIN. Filtering in WHERE removes rows where there’s no match (NULL), basically turning your LEFT JOIN into an INNER JOIN.

But the condition AND enrollments.status = 'active' inside ON limits the joined rows right away — it controls which rows even get into the join, not just filtering the result after.

This approach is super important if you want to keep rows from one table even if there’s no matching value in the other (which is common in reports and analytics).

More examples of ON ... AND ... usage

students table:

id name
1 Otto Song
2 Maria Chi
3 Alex Lin

enrollments table:

student_id course_id status enrolled_at
1 101 active 2025-02-01
1 103 active 2025-03-05
2 102 dropped 2024-05-15
3 101 active 2025-03-12

courses table:

id name
101 Math
102 Physics
103 CS

Example: only courses from the current year

SELECT
    students.name,
    courses.name,
    enrollments.enrolled_at
FROM students
JOIN enrollments
    ON students.id = enrollments.student_id
    AND EXTRACT(YEAR FROM enrollments.enrolled_at) = EXTRACT(YEAR FROM CURRENT_DATE)
JOIN courses
    ON enrollments.course_id = courses.id;

Here we’re joining only those records that are from the current year.

name name enrolled_at
Otto Song Math 2025-02-01
Otto Song CS 2025-03-05
Alex Lin Math 2025-03-12

Example: excluding by value

JOIN enrollments
    ON students.id = enrollments.student_id
    AND enrollments.status != 'dropped'

We’re excluding students who dropped out at the join stage, not filtering after.

name name
Otto Song Math
Otto Song CS
Alex Lin Math

When the condition is inside ON, PostgreSQL can optimize the join plan and process fewer rows. This is especially important with big data sets. Filtering inside is more efficient than filtering after the JOIN.

JOIN ON — it’s not just about keys

A lot of people think ON is just id = id. Actually, you can put in:

  • Logical operators: AND, OR, NOT
  • Comparisons: >, <, <>, BETWEEN, IN
  • Expressions: EXTRACT, DATE_TRUNC, COALESCE, NULLIF

Let’s combine it all

students table:

id name
1 Otto Song
2 Maria Chi
3 Alex Lin

faculties table:

id name
10 Engineering
20 Natural Sciences
30

Note: the faculty with id = 30 has no name (NULL).

courses table:

id name teacher faculty_id
101 Math Liam Park 10
102 Physics Chloe Zhang 20
103 CS Noah Kim 10
104 PE Ava Chen 30

enrollments table:

student_id course_id status
1 101 active
1 103 active
2 102 dropped
3 101 active
3 104 active
SELECT
    s.name AS student_name,
    c.name AS course_name,
    f.name AS faculty_name
FROM students s
JOIN enrollments e
    ON s.id = e.student_id
    AND e.status = 'active'
JOIN courses c
    ON e.course_id = c.id
    AND c.name != 'PE'
JOIN faculties f
    ON c.faculty_id = f.id
    AND f.name IS NOT NULL;

Here we’re filtering by:

  • Active records,
  • Courses except "PE",
  • Faculties that have a name.

Query result:

student_name course_name faculty_name
Otto Song Math Engineering
Otto Song CS Engineering
Alex Lin Math Engineering

Hope you liked this lecture. You’ll be using multiple JOINs with filters in your queries all the time. Pretty much always :)

2
Task
SQL SELF, level 12, lesson 2
Locked
Selecting Students with Active Courses
Selecting Students with Active Courses
2
Task
SQL SELF, level 12, lesson 2
Locked
Selecting records for the current year
Selecting records for the current year
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION