이미 JOIN으로 테이블을 합치는 방법은 알고 있지? 근데 실제로는 키 값만 맞춘다고 끝나는 게 아니야. 종종 추가 조건이 맞는 데이터만 합쳐야 할 때가 많아 — 예를 들어, 활성화된 데이터만, 올해 데이터만, 완료된 주문만 등등.
이럴 때 ON에 AND를 붙여서 조건을 확장하는 게 진짜 유용해.
JOIN ... ON에서 추가 조건을 쓰면 어떤 행이 조인에 들어갈지 정확하게 컨트롤할 수 있어. SQL이 결과를 만들기 전에 미리 걸러주니까, 쿼리가:
- 더 빨라져 (
JOIN을 통과하는 행이 적으니까), - 더 정확해져 (조인 단계에서 필터링하니까),
- 특히
LEFT JOIN에서 예측 가능해져 (WHERE에서 필터링하는 것과 다르게).
예시: 활성화된 수강신청만
예를 들어, enrollments 테이블에는 학생의 참여 상태가 있어: active, dropped, pending.
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처럼 돼버려.
반면에 ON 안에 AND enrollments.status = 'active'를 넣으면, 조인에 들어갈 행 자체를 제한하는 거라서 결과만 필터링하는 게 아니야.
이런 방식은 한쪽 테이블에 매칭되는 값이 없어도 행을 남기고 싶을 때(리포트나 분석에서 자주 쓰임) 특히 중요해.
다른 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만 넣는다고 생각하는데, 사실 여기에 이런 것도 넣을 수 있어:
- 논리 연산자:
AND,OR,NOT - 비교:
>,<,<>,BETWEEN,IN - 표현식:
EXTRACT,DATE_TRUNC,COALESCE,NULLIF
다 같이 조합해보자
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 != 'PE'
JOIN faculties f
ON c.faculty_id = f.id
AND f.name IS NOT NULL;
여기서 동시에 필터링하는 조건은:
- 활성화된 데이터,
- "PE" 과목 제외,
- 이름이 있는 학부만.
쿼리 결과:
| student_name | course_name | faculty_name |
|---|---|---|
| Otto Song | Math | Engineering |
| Otto Song | CS | Engineering |
| Alex Lin | Math | Engineering |
이 강의가 마음에 들었으면 좋겠다! 앞으로 쿼리 짤 때 여러 개의 JOIN이랑 필터를 진짜 자주 쓰게 될 거야. 거의 항상 :)
GO TO FULL VERSION