CodeGym /课程 /SQL SELF /SQL里的额外条件 JOINON ... AND ...

SQL里的额外条件 JOINON ... AND ...

SQL SELF
第 12 级 , 课程 2
可用

你已经知道怎么用 JOIN 来连接表了。但实际开发中,光靠主键匹配其实远远不够。经常会遇到这种需求:只连接那些还要满足额外条件的数据——比如只要活跃的记录、只要今年的数据、或者只要已完成的订单。

这时候就要用到 ON 里加 AND 的扩展写法了。

JOIN ... ON 里加额外条件,可以精确控制哪些行参与连接,而且是在 SQL 开始构建结果之前就筛选了。这样写的查询:

  • 更快(通过 JOIN 的行更少),
  • 更精准(过滤发生在连接阶段),
  • LEFT JOIN 时更可控(和 WHERE 过滤不一样)。

例子:只要活跃的课程报名

假设 enrollments 表里有学生报名状态:activedroppedpending

students 表:

id name
1 Otto Song
2 Maria Chi
3 Alex Lin

更新下 enrollments 表:

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

courses 表:

id name
101 Mathematics
102 Physics
103 Computer Science

现在我们想查只有那些有活跃课程的学生

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;

结果:

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

这里我们把 AND enrollments.status = 'active' 写在 ON,这样连接的时候只会连活跃的记录,而不是连完再过滤。

为啥不用 WHERE

你也可以这样写:

...
WHERE enrollments.status = 'active'

但这样LEFT JOIN 时行为就不一样了WHERE 过滤会把没匹配到的行(NULL)都删掉,这样 LEFT JOIN 就变成 INNER JOIN 了。

AND enrollments.status = 'active' 写在 ON 里,是直接限制参与连接的行——决定哪些行能进连接,不是连完再过滤。

这种写法特别重要,比如你想保留一张表里的所有行,即使另一张表没有匹配(做报表和分析时很常见)。

更多 ON ... AND ... 的用法例子

students 表:

id name
1 Otto Song
2 Maria Chi
3 Alex Lin

enrollments 表:

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 表:

id name
101 Math
102 Physics
103 CS

例子:只要今年的课程

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;

这里我们只连那些属于今年的记录。

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

例子:排除某个值

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

我们在连接阶段就把退课的学生排除了,而不是连完再过滤。

name name
Otto Song Math
Otto Song CS
Alex Lin Math

条件写在ON,PostgreSQL 可以优化连接计划,处理的行更少。数据量大时,这种内部过滤比连完再过滤高效多了。

JOIN ON —— 不只是主键

很多人以为 ON 只能写 id = id。其实你可以写:

  • 逻辑操作符:ANDORNOT
  • 比较:><<>BETWEENIN
  • 表达式:EXTRACTDATE_TRUNCCOALESCENULLIF

一起组合用

students 表:

id name
1 Otto Song
2 Maria Chi
3 Alex Lin

faculties 表:

id name
10 Engineering
20 Natural Sciences
30 ← 没有名字 (NULL)

courses 表:

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 表:

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 != 'Физкультура'
JOIN faculties f
    ON c.faculty_id = f.id
    AND f.name IS NOT NULL;

这里我们同时过滤:

  • 活跃记录,
  • 不是“Физкультура”的课程,
  • 有名字的院系。

查询结果:

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

希望你喜欢这节讲座。你以后写 SQL 查询时会经常用到多个带过滤的 JOIN。几乎每次都用得上,真的 :)

评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION