데이터는 보통 여러 테이블에 나뉘어 저장돼요. 사용자는 사용자대로, 주문은 주문대로요. 이걸 공통 열(키)로 연결해서 한 번에 보는 게 JOIN이에요. 이 장에서 INNER·LEFT·RIGHT 세 가지를 확실히 잡아봐요.
사용자 정보와 주문 정보를 한 테이블에 몰아넣으면 같은 사람 이름이 주문마다 반복돼 낭비되고 관리가 어려워요. 그래서 보통 주제별로 테이블을 나눠 저장해요(정규화). 대신 orders에는 "이 주문이 누구 것인지" 가리키는 외래 키(foreign key) user_id를 둬서, users.id와 연결할 수 있게 해요.
users.id가 기본 키죠. 방금 나온 외래 키는 바로 이 기본 키를 가리켜서 두 테이블을 잇는 거예요.| id | name |
|---|---|
| 1 | '철수' |
| 2 | '영희' |
| 3 | '민수' |
| id | user_id | product |
|---|---|---|
| 101 | 1 | '키보드' |
| 102 | 1 | '마우스' |
| 103 | 2 | '모니터' |
orders.user_id는 users.id를 가리키는 외래 키예요. 주문 101의 user_id가 1이니, 이건 철수의 주문이죠.users(id, name)와 orders(id, user_id, product)를 예로 써요. 핵심은 orders.user_id = users.id라는 연결 조건 하나예요.INNER JOIN은 두 테이블에서 연결 조건이 맞는(매칭되는) 행만 뽑아 이어 붙여요. ON 뒤에 어떤 열로 연결할지 조건을 적어요.
SELECT *
FROM users
INNER JOIN orders
ON users.id = orders.user_id; -- ON: 연결 조건
위 예제 데이터에서, users.id와 orders.user_id가 서로 맞는 행만 결과에 나와요. 주문이 있는 철수(1)·영희(2)만 나오고, 주문 없는 민수(3)는 빠져요.
| users.id | name | orders.id | user_id | product |
|---|---|---|---|---|
| 1 | '철수' | 101 | 1 | '키보드' |
| 1 | '철수' | 102 | 1 | '마우스' |
| 2 | '영희' | 103 | 2 | '모니터' |
INNER는 생략하고 그냥 JOIN이라 써도 같아요.LEFT JOIN은 왼쪽 테이블(FROM에 적은 쪽)의 모든 행을 남겨요. 오른쪽에 매칭되는 행이 있으면 이어 붙이고, 없으면 오른쪽 열을 NULL로 채워요. "주문이 없는 사용자까지 전부 보고 싶다"에 딱이에요.
SELECT *
FROM users
LEFT JOIN orders
ON users.id = orders.user_id;
이번엔 주문 없는 민수(3)도 나와요. 대신 매칭되는 주문이 없어서 orders 쪽 열은 NULL이에요.
| users.id | name | orders.id | user_id | product |
|---|---|---|---|---|
| 1 | '철수' | 101 | 1 | '키보드' |
| 1 | '철수' | 102 | 1 | '마우스' |
| 2 | '영희' | 103 | 2 | '모니터' |
| 3 | '민수' | NULL | NULL | NULL |
WHERE orders.id IS NULL로 걸러요.)RIGHT JOIN은 오른쪽 테이블의 모든 행을 남기고, 왼쪽에 매칭이 없으면 왼쪽을 NULL로 채워요. 사실 테이블 순서만 바꾸면 LEFT JOIN과 같은 결과라, 실무에선 LEFT를 훨씬 많이 써요.
SELECT *
FROM users
RIGHT JOIN orders
ON users.id = orders.user_id;
예제 데이터에선 모든 주문에 주인이 있어서, 위 RIGHT JOIN 결과는 INNER JOIN과 똑같이 3줄이에요(고아 주문이 없으니까요).
| 종류 | 남기는 행 | 매칭 없을 때 |
|---|---|---|
| INNER JOIN | 양쪽 다 매칭되는 행만 | 결과에서 빠짐 |
| LEFT JOIN | 왼쪽 전부 + 매칭되는 오른쪽 | 오른쪽을 NULL |
| RIGHT JOIN | 오른쪽 전부 + 매칭되는 왼쪽 | 왼쪽을 NULL |
ON 조건 없이 양쪽 행을 모두 짝지어 곱한 결과(왼쪽 3행 × 오른쪽 4행 = 12행)로, 곱집합(카티션 곱, cartesian product)이라 불러요. FULL OUTER JOIN은 양쪽 전부를 남기고 짝 없는 쪽을 NULL로 채워요(LEFT + RIGHT를 합친 셈이에요). 뒤 문제풀이에 잠깐 나오니 이름만 익혀 두세요.RIGHT JOIN을 지원하지 않았어요. 그래서 방향을 뒤집어 LEFT JOIN으로 쓰는 습관을 들이면 어디서든 안전해요.ON은 두 테이블을 어떤 열로 연결할지를 정하는 연결 조건이에요. WHERE가 "결과를 거르는" 조건이라면, ON은 "행을 이어 붙이는" 조건이라는 점이 달라요.
테이블 이름이 길면 별칭(alias)을 붙여 짧게 써요. users u, orders o처럼요. 그러면 열을 u.name, o.product로 간결하게 쓸 수 있어요.
SELECT u.name, o.product -- 별칭으로 간결하게
FROM users AS u
JOIN orders AS o
ON u.id = o.user_id
WHERE o.product = '키보드'; -- ON으로 이은 뒤 WHERE로 거름
users u처럼 AS 없이 바로 붙여도 돼요.id)이 있을 땐 u.id처럼 어느 테이블 것인지 반드시 밝혀야 해요.users→u, orders→o처럼 첫 글자를 자주 써요.
정리: ON = 잇는 조건, WHERE = 거르는 조건, 별칭 = 짧게 쓰기. 이 셋이면 조인 쿼리가 깔끔해져요.
orders.user_id = users.id)