-- 사용자 테이블
CREATE TABLE users (
id INT PRIMARY KEY,
name VARCHAR(50)
);
-- 주문 테이블
CREATE TABLE orders (
id INT PRIMARY KEY,
user_id INT, -- users.id 참조 (FK)
product VARCHAR(50)
);
users 테이블 orders 테이블
┌────┬────────┐ ┌────┬─────────┬──────────┐
│ id │ name │ │ id │ user_id │ product │
├────┼────────┤ ├────┼─────────┼──────────┤
│ 1 │ Alice │ │ 1 │ 1 │ iPhone │
│ 2 │ Bob │ │ 2 │ 1 │ iPad │
│ 3 │ Charlie│ │ 3 │ 2 │ MacBook │
│ 4 │ Dave │ │ 4 │ 99 │ AirPods │ ← user_id 99는 없는 유저
└────┴────────┘ └────┴─────────┴──────────┘
두 테이블 모두에 매칭되는 데이터만 반환합니다.
SELECT u.name, o.product
FROM users u
INNER JOIN orders o ON u.id = o.user_id;
-- ON 절: 어떤 컬럼을 기준으로 연결할지 조건 지정
결과
┌───────┬─────────┐
│ name │ product │
├───────┼─────────┤
│ Alice │ iPhone │
│ Alice │ iPad │
│ Bob │ MacBook │
└───────┴─────────┘
-- Charlie, Dave: 주문 없음 → 제외
-- user_id=99 주문: 유저 없음 → 제외
활용 예시: 실제로 주문한 사용자와 주문 내역을 함께 조회할 때
왼쪽 테이블은 모두 반환하고, 오른쪽에 매칭이 없으면
NULL로 채웁니다.
SELECT u.name, o.product
FROM users u
LEFT JOIN orders o ON u.id = o.user_id;
-- users(왼쪽)를 기준으로 모두 포함
결과
┌─────────┬─────────┐
│ name │ product │
├─────────┼─────────┤
│ Alice │ iPhone │
│ Alice │ iPad │
│ Bob │ MacBook │
│ Charlie │ NULL │ ← 주문이 없어도 포함
│ Dave │ NULL │ ← 주문이 없어도 포함
└─────────┴─────────┘
활용 예시: 주문 여부와 관계없이 모든 사용자 목록을 조회할 때, 또는 주문 안 한 유저만 필터링할 때
-- 한 번도 주문하지 않은 사용자 조회
SELECT u.name
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE o.id IS NULL; -- NULL인 경우 = 매칭되는 주문이 없는 경우
오른쪽 테이블은 모두 반환하고, 왼쪽에 매칭이 없으면
NULL로 채웁니다.
SELECT u.name, o.product
FROM users u
RIGHT JOIN orders o ON u.id = o.user_id;
-- orders(오른쪽)를 기준으로 모두 포함
결과
┌───────┬─────────┐
│ name │ product │
├───────┼─────────┤
│ Alice │ iPhone │
│ Alice │ iPad │
│ Bob │ MacBook │
│ NULL │ AirPods │ ← user_id=99 유저가 없어도 주문은 포함
└───────┴─────────┘
활용 예시: 유저 정보가 삭제됐더라도 주문 데이터를 전부 확인할 때
💡 대부분의 상황에서는 LEFT JOIN을 훨씬 더 많이 씁니다. RIGHT JOIN은 테이블 순서를 바꿔서 LEFT JOIN으로 대체 가능함을 기억.
양쪽 테이블 모두를 반환하고, 매칭 없는 쪽은
NULL로 채웁니다.
SELECT u.name, o.product
FROM users u
FULL OUTER JOIN orders o ON u.id = o.user_id;
결과
┌─────────┬─────────┐
│ name │ product │
├─────────┼─────────┤
│ Alice │ iPhone │
│ Alice │ iPad │
│ Bob │ MacBook │
│ Charlie │ NULL │
│ Dave │ NULL │
│ NULL │ AirPods │
└─────────┴─────────┘
⚠️
MySQL등 일부 RDBMS는FULL OUTER JOIN을 지원하지 않습니다.LEFT JOIN+RIGHT JOIN을UNION으로 합쳐야 합니다.
-- MySQL에서 FULL OUTER JOIN 대체
SELECT u.name, o.product FROM users u LEFT JOIN orders o ON u.id = o.user_id
UNION
SELECT u.name, o.product FROM users u RIGHT JOIN orders o ON u.id = o.user_id;
모든 조합(카테시안 곱)을 반환합니다. 조건 없이 양쪽을 곱합니다.
SELECT u.name, o.product
FROM users u
CROSS JOIN orders o;
-- users 4행 × orders 4행 = 16행 반환
활용 예시: 모든 상품 × 모든 사용자 조합이 필요한 경우 (ex. 추천 시스템 초기 데이터 생성)
같은 테이블을 자기 자신과 조인합니다. 별칭(alias)을 다르게 줘서 사용합니다.
-- 직원 테이블 (manager_id가 같은 테이블의 id를 참조)
SELECT e.name AS 직원, m.name AS 매니저
FROM employees e
LEFT JOIN employees m ON e.manager_id = m.id;
-- e와 m 모두 같은 employees 테이블이지만 별칭을 다르게 부여
활용 예시: 계층 구조(조직도, 카테고리 부모-자식 관계) 표현
두 개 이상의
SELECT결과를 세로로 합치는 연산자입니다.
JOIN이 테이블을 가로로 연결한다면, UNION은 결과를 위아래로 쌓는 개념입니다.
기본 문법
SELECT 컬럼1, 컬럼2 FROM 테이블A
UNION
SELECT 컬럼1, 컬럼2 FROM 테이블B;
-- 컬럼 수와 타입이 일치해야 합니다
-- 예시 데이터
-- tableA: [1, 2, 3]
-- tableB: [2, 3, 4]
SELECT num FROM tableA
UNION
SELECT num FROM tableB;
-- 결과: 1, 2, 3, 4 ← 중복 제거됨
SELECT num FROM tableA
UNION ALL
SELECT num FROM tableB;
-- 결과: 1, 2, 3, 2, 3, 4 ← 중복 그대로
실용 예시 - 이전 FULL OUTER JOIN 대체
-- 왼쪽 기준으로 모두 가져오고
SELECT u.name, o.product
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
UNION -- 중복 제거하면서 합치기
-- 오른쪽 기준으로 모두 가져오기
SELECT u.name, o.product
FROM users u
RIGHT JOIN orders o ON u.id = o.user_id;
정리
JOIN → 테이블을 옆으로 붙이기UNION → 결과를 아래로 쌓기UNION, 성능이 중요하면 UNION ALL