SQL JOIN 종류 정리: INNER, LEFT, RIGHT, OUTER 완벽 이해
SQL JOIN이란 무엇인가
SQL JOIN은 두 개 이상의 테이블을 특정 조건(주로 공통 컬럼)으로 연결해 하나의 결과 집합으로 만드는 기능입니다. 관계형 데이터베이스는 데이터를 여러 테이블에 나눠 저장하기 때문에, 실무 쿼리의 절반 이상이 JOIN을 사용한다고 봐도 됩니다. 이 글에서는 SQL JOIN의 종류인 INNER JOIN, LEFT JOIN, RIGHT JOIN, FULL OUTER JOIN을 예제와 함께 정리합니다.
예제를 위해 회원(users)과 주문(orders) 두 테이블을 사용하겠습니다. 주문이 없는 회원도 있고, 회원이 탈퇴해 연결이 끊긴 주문도 있다고 가정합니다.
-- users
id | name
1 | 김철수
2 | 이영희
3 | 박민수 (주문 없음)
-- orders
id | user_id | amount
10 | 1 | 5000
11 | 1 | 3000
12 | 2 | 7000
13 | 99 | 9000 (없는 회원)SQL JOIN 종류별 문법과 예제
INNER JOIN (교집합)
INNER JOIN은 양쪽 테이블에서 조인 조건이 모두 일치하는 행만 반환합니다. 가장 많이 쓰이는 조인이며, 단순히 JOIN이라고만 써도 INNER JOIN으로 동작합니다.
SELECT u.name, o.amount
FROM users u
INNER JOIN orders o ON u.id = o.user_id;
-- 결과
김철수 | 5000
김철수 | 3000
이영희 | 7000주문이 없는 박민수(3번)와, 회원이 없는 주문(user_id 99)은 양쪽에 짝이 없으므로 결과에서 제외됩니다.
LEFT JOIN (왼쪽 기준)
LEFT JOIN(정식 명칭 LEFT OUTER JOIN)은 왼쪽 테이블의 모든 행을 유지하고, 오른쪽에 일치하는 값이 없으면 NULL로 채웁니다. "모든 회원과 그 주문을 보여주되, 주문이 없는 회원도 빠뜨리지 마라"는 요구에 적합합니다.
SELECT u.name, o.amount
FROM users u
LEFT JOIN orders o ON u.id = o.user_id;
-- 결과
김철수 | 5000
김철수 | 3000
이영희 | 7000
박민수 | NULL -- 주문이 없어 NULL주문이 없는 회원만 찾고 싶다면 다음과 같이 NULL 조건을 추가합니다.
SELECT u.name
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE o.id IS NULL;
-- 결과: 박민수RIGHT JOIN (오른쪽 기준)
RIGHT JOIN은 LEFT JOIN의 방향만 반대입니다. 오른쪽 테이블의 모든 행을 유지하고, 왼쪽에 짝이 없으면 NULL을 채웁니다.
SELECT u.name, o.amount
FROM users u
RIGHT JOIN orders o ON u.id = o.user_id;
-- 결과
김철수 | 5000
김철수 | 3000
이영희 | 7000
NULL | 9000 -- user_id 99, 회원이 없어 NULL실무에서는 RIGHT JOIN을 잘 쓰지 않습니다. 테이블 순서를 바꿔 LEFT JOIN으로 표현하는 편이 읽기 쉽기 때문입니다.
FULL OUTER JOIN (합집합)
FULL OUTER JOIN은 양쪽 테이블의 모든 행을 유지하며, 어느 한쪽에 짝이 없으면 그 부분을 NULL로 채웁니다.
SELECT u.name, o.amount
FROM users u
FULL OUTER JOIN orders o ON u.id = o.user_id;
-- 결과
김철수 | 5000
김철수 | 3000
이영희 | 7000
박민수 | NULL
NULL | 9000참고로 MySQL은 FULL OUTER JOIN을 직접 지원하지 않습니다. LEFT JOIN 결과와 RIGHT JOIN 결과를 UNION으로 합쳐 흉내 냅니다. PostgreSQL, SQL Server, Oracle은 정식으로 지원합니다.
JOIN 종류 비교표
| JOIN 종류 | 반환되는 행 | NULL 발생 위치 |
|---|---|---|
| INNER JOIN | 양쪽 모두 일치하는 행 | 없음 |
| LEFT JOIN | 왼쪽 전체 + 일치하는 오른쪽 | 오른쪽 |
| RIGHT JOIN | 오른쪽 전체 + 일치하는 왼쪽 | 왼쪽 |
| FULL OUTER JOIN | 양쪽 전체 | 양쪽 |
실전 팁
첫째, 테이블에는 항상 별칭(alias)을 붙이세요. users u처럼 짧은 별칭을 쓰면 u.id, o.user_id로 컬럼 출처가 명확해집니다. 둘째, JOIN 조건은 ON 절에, 필터 조건은 WHERE 절에 두는 것을 기본으로 합니다. 셋째, 조인 컬럼(여기서는 user_id)에 인덱스가 있으면 성능이 크게 향상됩니다. 대량 데이터에서 조인이 느리다면 가장 먼저 인덱스를 확인하세요.
흔한 실수
1. LEFT JOIN인데 WHERE로 NULL을 걸러버리는 실수. LEFT JOIN orders o ... WHERE o.amount > 1000처럼 오른쪽 테이블 컬럼을 WHERE에 쓰면, NULL 행이 조건에서 탈락해 사실상 INNER JOIN이 됩니다. 오른쪽 테이블 조건은 ON o.amount > 1000처럼 ON 절에 넣어야 LEFT JOIN의 의미가 유지됩니다.
2. 카테시안 곱(중복 폭증). ON 조건을 빠뜨리면 모든 행이 서로 곱해져 행 수가 폭발합니다. 또한 일대다 관계를 조인하면 결과 행이 늘어나므로, 합계를 구할 때 값이 중복 계산되지 않도록 주의해야 합니다.
자주 묻는 질문
Q1. JOIN과 INNER JOIN은 다른가요?
같습니다. 표준 SQL에서 JOIN만 쓰면 INNER JOIN으로 해석됩니다. 다만 의도를 분명히 하려면 INNER JOIN이라고 명시하는 것을 권장합니다.
Q2. LEFT JOIN과 LEFT OUTER JOIN은 차이가 있나요?
없습니다. OUTER는 선택적 키워드라 생략해도 동일하게 동작합니다. RIGHT JOIN, FULL JOIN도 마찬가지입니다.
Q3. 세 개 이상의 테이블도 조인할 수 있나요?
네. A JOIN B ON ... JOIN C ON ...처럼 JOIN을 연달아 붙이면 됩니다. 다만 테이블이 많아질수록 조인 순서와 조건을 명확히 관리해야 결과가 의도대로 나옵니다.