INNER JOIN과 LEFT JOIN의 차이는 문법이 아니라 조회 기준에 있다. INNER JOIN은 양쪽 테이블에 모두 연결되는 데이터만 조회하고, LEFT JOIN은 왼쪽 테이블을 기준으로 전체 데이터를 조회한다. 그래서 SQL JOIN이 헷갈릴 때는 문법보다 먼저 **“내가 전체를 보고 싶은지, 연결된 데이터만 보고 싶은지”**를 정해야 한다.
실무에서 이 차이는 생각보다 자주 문제를 만든다. 예를 들어 “동아리에 가입한 학생만 보고 싶은지”, “전체 학생 중 누가 가입하지 않았는지까지 보고 싶은지”에 따라 JOIN 결과가 완전히 달라진다. 이 글에서는 학생-동아리 예제로 INNER JOIN과 LEFT JOIN 차이를 MySQL 기준으로 정리한다.
INNER JOIN과 LEFT JOIN을 헷갈리는 이유
두 JOIN은 둘 다 ON 조건으로 테이블을 연결한다. 그래서 처음 보면 문법이 거의 비슷해서 차이가 작아 보인다. 하지만 실제 결과는 꽤 다르다.
핵심은 딱 하나다.
- INNER JOIN: 연결되는 데이터만 남긴다
- LEFT JOIN: 왼쪽 테이블은 무조건 남긴다
즉, JOIN은 단순히 테이블을 붙이는 문법이 아니라 어떤 데이터를 결과에 남길지 정하는 방식이다.
예시 테이블 구조
이번 예시는 학생과 동아리 데이터를 다음처럼 나눈 구조를 기준으로 본다.
- stdtbl: 학생 정보
- clubtbl: 동아리 정보
- stdclubtbl: 학생-동아리 연결 정보
이 구조를 쓰는 이유는 학생과 동아리가 다대다 관계이기 때문이다. 학생 1명은 여러 동아리에 가입할 수 있고, 동아리 1개에도 여러 학생이 가입할 수 있다. 그래서 중간 연결 테이블이 필요하다.
예를 들어 각 테이블은 이런 역할을 가진다.
- stdtbl: 학생 이름, 주소
- clubtbl: 동아리 이름, 동아리방 번호
- stdclubtbl: 어떤 학생이 어떤 동아리에 가입했는지
이제 이 데이터를 어떻게 보고 싶은지에 따라 JOIN이 달라진다.
1. 동아리에 가입한 학생만 보고 싶다면 INNER JOIN
동아리에 실제로 가입한 학생만 보고 싶다면 INNER JOIN이 맞다.
SELECT S.stdName, S.addr, SC.clubName, C.roomNo
FROM stdtbl S
INNER JOIN stdclubtbl SC
ON S.stdName = SC.stdName
INNER JOIN clubtbl C
ON C.clubName = SC.clubName;
이 쿼리는 학생 테이블과 연결 테이블, 그리고 동아리 테이블을 차례로 연결한다. 여기서 중요한 점은 중간에 연결되지 않는 학생은 결과에서 빠진다는 것이다.
예를 들어 전체 학생이 5명인데 실제로 동아리 가입 이력이 있는 학생이 3명이라면, 결과는 그 3명만 나온다. INNER JOIN은 양쪽 테이블에 모두 존재하는 데이터만 조회하기 때문이다.
즉 이런 상황이면 INNER JOIN이다.
- 신청한 사람만 보고 싶다
- 연결된 데이터만 의미 있다
- 미가입자는 굳이 결과에 필요 없다
2. 전체 학생 기준으로 가입 여부를 보고 싶다면 LEFT JOIN
전체 학생을 기준으로 누가 동아리에 가입했고, 누가 가입하지 않았는지까지 보고 싶다면 LEFT JOIN을 써야 한다.
SELECT S.stdName, SC.clubName, C.roomNo
FROM stdtbl S
LEFT JOIN stdclubtbl SC
ON S.stdName = SC.stdName
LEFT JOIN clubtbl C
ON SC.clubName = C.clubName;
이 쿼리에서는 stdtbl이 왼쪽 테이블이다. 그래서 학생 테이블의 데이터는 모두 결과에 남는다. 만약 동아리 가입 이력이 없는 학생이 있다면, 그 학생도 결과에 나오고 동아리 관련 컬럼만 NULL로 표시된다.
즉 LEFT JOIN은 이런 상황에서 쓴다.
- 전체 학생을 기준으로 보고 싶다
- 신청 여부까지 확인하고 싶다
- 연결되지 않은 행도 분석 대상이다
LEFT JOIN에서 NULL이 나오는 이유
LEFT JOIN 결과에서 NULL이 보이면 대부분 연결 정보가 없는 경우다. 예를 들어 학생은 존재하지만 stdclubtbl에 연결된 데이터가 없으면, 오른쪽 테이블에서 가져올 값이 없기 때문에 clubName, roomNo는 NULL이 된다.
예를 들어 결과가 이런 식으로 나올 수 있다.
stdName clubName roomNo
| 성시경 | NULL | NULL |
이건 오류가 아니라 정상 동작이다. 오히려 이 NULL 덕분에 “아직 동아리에 가입하지 않은 학생”을 바로 찾을 수 있다.
예를 들어 미가입 학생만 조회하려면 이렇게 쓸 수 있다.
SELECT S.stdName
FROM stdtbl S
LEFT JOIN stdclubtbl SC
ON S.stdName = SC.stdName
WHERE SC.clubName IS NULL;
이 쿼리는 전체 학생 중 동아리 가입 이력이 없는 학생만 골라낸다.
INNER JOIN과 LEFT JOIN 차이 한 번에 정리
아래 표처럼 보면 가장 이해가 빠르다.
구분 INNER JOIN LEFT JOIN
| 조회 기준 | 양쪽 테이블에 모두 존재하는 데이터 | 왼쪽 테이블 전체 |
| 연결 실패한 데이터 | 결과에서 제외 | 오른쪽 컬럼이 NULL |
| 주로 쓰는 상황 | 신청한 사람만 보고 싶을 때 | 전체 기준으로 신청 여부를 보고 싶을 때 |
| 결과 특징 | 필터링된 연결 결과 | 기준 데이터는 유지됨 |
즉 JOIN 종류를 고를 때는 이렇게 생각하면 된다.
- 연결된 데이터만 필요하다 → INNER JOIN
- 기준 테이블 전체를 유지해야 한다 → LEFT JOIN
JOIN에서 가장 중요한 건 기준 테이블이다
JOIN을 배울 때 많이 놓치는 포인트가 있다. LEFT JOIN에서 중요한 건 “왼쪽”이라는 방향 자체가 아니라 왼쪽에 어떤 테이블을 두느냐는 점이다.
예를 들어 학생 전체를 보고 싶으면 stdtbl을 왼쪽에 두면 되고, 동아리 전체를 기준으로 보고 싶으면 clubtbl을 왼쪽에 둘 수도 있다. 즉 LEFT JOIN은 “왼쪽 조인”이 아니라 기준 테이블 전체를 살리는 조인이라고 이해하는 게 더 실용적이다.
JOIN이 헷갈릴 때는 이렇게 먼저 물어보면 된다.
- 내가 기준으로 보고 싶은 테이블은 무엇인가?
- 연결 안 된 데이터도 결과에 남겨야 하는가?
- 누락된 대상을 확인해야 하는가?
이 질문에 답하면 INNER JOIN과 LEFT JOIN은 대부분 자연스럽게 정해진다.
실전에서 자주 하는 실수
1) LEFT JOIN을 써놓고 WHERE에서 오른쪽 테이블 조건을 바로 거는 경우
이건 정말 흔하다. LEFT JOIN을 해놓고 WHERE 절에서 오른쪽 테이블 컬럼 조건을 걸어버리면 NULL 행이 제거되면서 사실상 INNER JOIN처럼 동작할 수 있다.
2) 연결 테이블 없이 바로 JOIN하려는 경우
학생과 동아리처럼 다대다 관계는 중간 연결 테이블이 필요하다. stdtbl과 clubtbl을 바로 붙이려고 하면 구조 자체가 꼬인다.
3) JOIN을 문법으로만 외우는 경우
INNER JOIN은 이렇게, LEFT JOIN은 저렇게만 외우면 실제 문제를 풀 때 헷갈린다. JOIN은 흩어진 정보를 어떤 기준으로 연결할지 정하는 과정으로 이해해야 오래 간다.
정리
INNER JOIN과 LEFT JOIN의 차이는 생각보다 단순하다. INNER JOIN은 연결되는 데이터만 조회하고, LEFT JOIN은 왼쪽 테이블을 기준으로 전체 데이터를 조회한다. 결국 SQL JOIN에서 가장 중요한 건 ON 조건보다 먼저 기준 테이블을 무엇으로 잡을지다.
JOIN 결과가 예상과 다르게 나올 때도 대부분 원인은 이 둘 중 하나다.
- 연결되지 않은 데이터가 빠진 것인지
- 기준 테이블 전체를 유지했는지
이 두 가지만 정확히 보면 INNER JOIN과 LEFT JOIN은 훨씬 덜 헷갈린다.
INNER JOIN은 연결된 데이터만, LEFT JOIN은 기준 테이블 전체를 조회한다. JOIN을 고를 때는 문법보다 먼저 무엇을 기준으로 볼지부터 정하면 된다.
'Data Analytics > SQL' 카테고리의 다른 글
| SQL 서브쿼리 주의점 | 최고값 비교할 때 조건 범위를 맞춰야 하는 이유 (0) | 2026.05.26 |
|---|---|
| SQL JOIN으로 N:M 관계 풀기 | 학생-동아리 연결 테이블 예제 (0) | 2026.05.20 |
| SQL JOIN 정리 | INNER JOIN, LEFT JOIN, RIGHT JOIN 차이 이해하기 (0) | 2026.05.20 |
| SQL ORDER BY와 LIMIT 정리 | 정렬하고 상위 N개 데이터 조회하기 (0) | 2026.05.19 |
| SQL WHERE와 HAVING 차이 정리 | 원본 데이터 필터와 집계 결과 필터 (0) | 2026.05.19 |
