INNER, LEFT, RIGHT

💡 SQL에서 가장 중요한 개념 중 하나인 JOIN을 완벽하게 정리한다.
두 개 이상의 테이블을 연결해서 데이터를 가져오는 것.
현실에서 데이터는 여러 테이블에 나뉘어 저장된다:
users 테이블: 유저 정보orders 테이블: 주문 정보products 테이블: 상품 정보→ "어떤 유저가 어떤 상품을 샀는지" 알려면? JOIN 필요!
이 글에서 사용할 테이블:
[users]
| user_id | name |
|---------|------|
| 1 | 김철수 |
| 2 | 이영희 |
| 3 | 박민수 |
| 4 | 최지은 |
[orders]
| order_id | user_id | product | amount |
|----------|---------|---------|--------|
| 101 | 1 | 노트북 | 1500000 |
| 102 | 1 | 마우스 | 50000 |
| 103 | 2 | 키보드 | 100000 |
| 104 | 5 | 모니터 | 300000 |
포인트:
.
.
.
.
.
.
.
[INNER JOIN] — 양쪽 다 있는 것만 [LEFT JOIN] — 왼쪽 전부 + 매칭되는 오른쪽 [RIGHT JOIN] — 오른쪽 전부 + 매칭되는 왼쪽 [FULL JOIN] — 양쪽 전부 (합집합) [CROSS JOIN] — 모든 조합 (곱집합)
양쪽 테이블에 모두 존재하는 데이터만 가져온다.
[users] [orders]
1 ──────── 1
2 ──────── 2
3 5
4
→ 결과: 1, 2만 (교집합)
SELECT *
FROM users u
INNER JOIN orders o ON u.user_id = o.user_id;
또는 줄여서:
SELECT *
FROM users u
JOIN orders o ON u.user_id = o.user_id;
INNER는 생략 가능!JOIN만 쓰면 기본이 INNER JOIN
| user_id | name | order_id | product | amount |
|---|---|---|---|---|
| 1 | 김철수 | 101 | 노트북 | 1500000 |
| 1 | 김철수 | 102 | 마우스 | 50000 |
| 2 | 이영희 | 103 | 키보드 | 100000 |
사라진 것:
왼쪽 테이블은 전부 가져오고, 오른쪽은 매칭되는 것만.
[users] [orders]
1 ──────── 1
2 ──────── 2
3 ──→ NULL
4 ──→ NULL
→ 결과: 1, 2, 3, 4 (왼쪽 전부)
SELECT *
FROM users u
LEFT JOIN orders o ON u.user_id = o.user_id;
| user_id | name | order_id | product | amount |
|---|---|---|---|---|
| 1 | 김철수 | 101 | 노트북 | 1500000 |
| 1 | 김철수 | 102 | 마우스 | 50000 |
| 2 | 이영희 | 103 | 키보드 | 100000 |
| 3 | 박민수 | NULL | NULL | NULL |
| 4 | 최지은 | NULL | NULL | NULL |
포인트: 주문 없는 유저도 나옴! (NULL로 채워짐)
SELECT u.user_id, u.name
FROM users u
LEFT JOIN orders o ON u.user_id = o.user_id
WHERE o.order_id IS NULL;
| user_id | name |
|---|---|
| 3 | 박민수 |
| 4 | 최지은 |
→ LEFT JOIN + WHERE NULL 패턴 자주 나옴!
LEFT JOIN의 반대. 오른쪽 테이블 전부 + 왼쪽 매칭.
[users] [orders]
1 ──────── 1
2 ──────── 2
NULL ←── 5
→ 결과: 1, 2, 5 (오른쪽 전부)
SELECT *
FROM users u
RIGHT JOIN orders o ON u.user_id = o.user_id;
| user_id | name | order_id | product | amount |
|---|---|---|---|---|
| 1 | 김철수 | 101 | 노트북 | 1500000 |
| 1 | 김철수 | 102 | 마우스 | 50000 |
| 2 | 이영희 | 103 | 키보드 | 100000 |
| NULL | NULL | 104 | 모니터 | 300000 |
실무에서 거의 안 씀! LEFT JOIN으로 테이블 순서만 바꾸면 됨.
-- 이것과
SELECT * FROM A RIGHT JOIN B ON ...
-- 이건 같은 결과
SELECT * FROM B LEFT JOIN A ON ...
→ LEFT JOIN만 잘 쓰면 됨
양쪽 테이블 전부 가져온다. (합집합)
[users] [orders]
1 ──────── 1
2 ──────── 2
3 ──→ NULL
4 ──→ NULL
NULL ←── 5
→ 결과: 1, 2, 3, 4, 5 (전부)
SELECT *
FROM users u
FULL OUTER JOIN orders o ON u.user_id = o.user_id;
| user_id | name | order_id | product | amount |
|---|---|---|---|---|
| 1 | 김철수 | 101 | 노트북 | 1500000 |
| 1 | 김철수 | 102 | 마우스 | 50000 |
| 2 | 이영희 | 103 | 키보드 | 100000 |
| 3 | 박민수 | NULL | NULL | NULL |
| 4 | 최지은 | NULL | NULL | NULL |
| NULL | NULL | 104 | 모니터 | 300000 |
MySQL에서 구현하려면:
SELECT * FROM users u LEFT JOIN orders o ON u.user_id = o.user_id
UNION
SELECT * FROM users u RIGHT JOIN orders o ON u.user_id = o.user_id;
모든 조합을 만든다. (곱집합)
users 4행 × orders 4행 = 16행
SELECT *
FROM users
CROSS JOIN orders;
또는:
SELECT *
FROM users, orders;
예: 모든 날짜 × 모든 상품 조합
SELECT d.date, p.product_name
FROM dates d
CROSS JOIN products p;
| JOIN 종류 | 결과 | 쉬운 설명 |
|---|---|---|
| INNER | 교집합 | 둘 다 있는 것만 |
| LEFT | 왼쪽 전부 | 왼쪽 기준 |
| RIGHT | 오른쪽 전부 | (거의 안 씀) |
| FULL | 합집합 | 다 가져와 |
| CROSS | 곱집합 | 모든 조합 |
실무 빈도:
INNER JOIN ████████████ 40%
LEFT JOIN ████████████████ 55%
나머지 ██ 5%
→ INNER, LEFT만 확실히 알면 90% 커버!
정답 보기모든 주문에 대해 주문자 이름과 상품명, 금액을 조회하세요.
SELECT u.name, o.product, o.amount
FROM orders o
JOIN users u ON o.user_id = u.user_id;
정답 보기모든 유저의 총 주문 금액을 구하세요. 주문이 없는 유저는 0으로 표시.
SELECT
u.name,
COALESCE(SUM(o.amount), 0) AS total_amount
FROM users u
LEFT JOIN orders o ON u.user_id = o.user_id
GROUP BY u.user_id, u.name;
| name | total_amount |
|---|---|
| 김철수 | 1550000 |
| 이영희 | 100000 |
| 박민수 | 0 |
| 최지은 | 0 |
포인트: COALESCE로 NULL을 0으로 변환!
정답 보기한 번도 주문하지 않은 유저를 찾으세요.
SELECT u.user_id, u.name
FROM users u
LEFT JOIN orders o ON u.user_id = o.user_id
WHERE o.order_id IS NULL;
-- ON에 조건 (JOIN 전에 필터링)
SELECT *
FROM users u
LEFT JOIN orders o ON u.user_id = o.user_id
AND o.amount > 100000;
-- WHERE에 조건 (JOIN 후에 필터링)
SELECT *
FROM users u
LEFT JOIN orders o ON u.user_id = o.user_id
WHERE o.amount > 100000;
LEFT JOIN에서 결과가 다를 수 있음! 주의!
-- 길게
SELECT users.name, orders.product
FROM users
JOIN orders ON users.user_id = orders.user_id;
-- 짧게 (권장)
SELECT u.name, o.product
FROM users u
JOIN orders o ON u.user_id = o.user_id;
SELECT
u.name,
o.order_id,
p.product_name,
p.price
FROM users u
JOIN orders o ON u.user_id = o.user_id
JOIN products p ON o.product_id = p.product_id;
→ 체인처럼 연결!
[SQL 시리즈 #2] GROUP BY & 집계함수 — 데이터 요약의 기술
📝 JOIN은 SQL의 핵심이다.
INNER JOIN과 LEFT JOIN만 확실히 이해해도 대부분의 상황에 대응할 수 있다.
실전에서는 "어느 테이블을 기준으로 할 것인가"를 먼저 생각하자!