DevToolBox무료
블로그

SQL Join 완벽 가이드: 시각적 설명과 예제

12분 읽기by DevToolBox

SQL 조인은 관계형 데이터베이스의 핵심입니다. 관련 열을 기반으로 두 개 이상의 테이블에서 데이터를 결합할 수 있습니다. 기본적인 개념임에도 많은 개발자에게 가장 혼란스러운 주제 중 하나입니다. 이 가이드는 시각적 다이어그램과 실용적인 예제를 사용하여 각 조인 유형을 명확하게 설명합니다.

샘플 데이터: 두 개의 테이블

이 가이드에서는 다음 두 개의 간단한 테이블을 사용합니다:

-- employees table
+----+----------+---------------+
| id | name     | department_id |
+----+----------+---------------+
| 1  | Alice    | 1             |
| 2  | Bob      | 2             |
| 3  | Charlie  | 1             |
| 4  | Diana    | NULL          |
+----+----------+---------------+

-- departments table
+----+-------------+
| id | dept_name   |
+----+-------------+
| 1  | Engineering |
| 2  | Marketing   |
| 3  | Sales       |
+----+-------------+

INNER JOIN (내부 조인)

두 테이블 모두에서 일치하는 값이 있는 행만 반환합니다. 두 집합의 교집합으로 생각하세요.

    Table A         Table B
  +---------+     +---------+
  |         |     |         |
  |    +----+-----+----+    |
  |    |  INNER JOIN   |    |
  |    +----+-----+----+    |
  |         |     |         |
  +---------+     +---------+
SELECT e.name, d.dept_name
FROM employees e
INNER JOIN departments d ON e.department_id = d.id;

결과:

+----------+-------------+
| name     | dept_name   |
+----------+-------------+
| Alice    | Engineering |
| Bob      | Marketing   |
| Charlie  | Engineering |
+----------+-------------+
-- Diana excluded (department_id is NULL)
-- Sales excluded (no employee in Sales)

두 테이블 모두에 존재하는 레코드만 필요할 때 사용합니다.

LEFT JOIN (왼쪽 외부 조인)

왼쪽 테이블의 모든 행과 오른쪽 테이블의 일치하는 행을 반환합니다. 일치하지 않으면 NULL이 반환됩니다.

    Table A         Table B
  +---------+     +---------+
  |XXXXXXXXX|     |         |
  |XXXX+----+-----+----+    |
  |XXXX| LEFT JOIN XXXX|    |
  |XXXX+----+-----+----+    |
  |XXXXXXXXX|     |         |
  +---------+     +---------+
  (all of A + matching B)
SELECT e.name, d.dept_name
FROM employees e
LEFT JOIN departments d ON e.department_id = d.id;

결과:

+----------+-------------+
| name     | dept_name   |
+----------+-------------+
| Alice    | Engineering |
| Bob      | Marketing   |
| Charlie  | Engineering |
| Diana    | NULL        |
+----------+-------------+
-- Diana included with NULL dept_name

일치 여부와 관계없이 왼쪽 테이블의 모든 레코드가 필요할 때 사용합니다.

RIGHT JOIN (오른쪽 외부 조인)

오른쪽 테이블의 모든 행과 왼쪽 테이블의 일치하는 행을 반환합니다.

    Table A         Table B
  +---------+     +---------+
  |         |     |XXXXXXXXX|
  |    +----+-----+XXXX+XXXX|
  |    |XXXX RIGHT JOINXXXX|
  |    +----+-----+XXXX+XXXX|
  |         |     |XXXXXXXXX|
  +---------+     +---------+
  (matching A + all of B)
SELECT e.name, d.dept_name
FROM employees e
RIGHT JOIN departments d ON e.department_id = d.id;

결과:

+----------+-------------+
| name     | dept_name   |
+----------+-------------+
| Alice    | Engineering |
| Charlie  | Engineering |
| Bob      | Marketing   |
| NULL     | Sales       |
+----------+-------------+
-- Sales included with NULL name

오른쪽 테이블의 모든 레코드가 필요할 때 사용합니다.

FULL OUTER JOIN (완전 외부 조인)

두 테이블의 모든 행을 반환합니다. 일치하지 않는 곳은 NULL로 채웁니다.

    Table A         Table B
  +---------+     +---------+
  |XXXXXXXXX|     |XXXXXXXXX|
  |XXXX+----+-----+XXXX+XXXX|
  |XXXX|FULL OUTER JOINXXXX|
  |XXXX+----+-----+XXXX+XXXX|
  |XXXXXXXXX|     |XXXXXXXXX|
  +---------+     +---------+
  (everything from both)
SELECT e.name, d.dept_name
FROM employees e
FULL OUTER JOIN departments d ON e.department_id = d.id;

결과:

+----------+-------------+
| name     | dept_name   |
+----------+-------------+
| Alice    | Engineering |
| Bob      | Marketing   |
| Charlie  | Engineering |
| Diana    | NULL        |
| NULL     | Sales       |
+----------+-------------+

두 테이블의 모든 레코드가 필요할 때 사용합니다.

CROSS JOIN (교차 조인)

두 테이블의 카르테시안 곱을 반환합니다. ON 절이 필요 없습니다.

SELECT e.name, d.dept_name
FROM employees e
CROSS JOIN departments d;

-- Returns 4 × 3 = 12 rows

결과:

+----------+-------------+
| name     | dept_name   |
+----------+-------------+
| Alice    | Engineering |
| Alice    | Marketing   |
| Alice    | Sales       |
| Bob      | Engineering |
| Bob      | Marketing   |
| Bob      | Sales       |
| Charlie  | Engineering |
| Charlie  | Marketing   |
| Charlie  | Sales       |
| Diana    | Engineering |
| Diana    | Marketing   |
| Diana    | Sales       |
+----------+-------------+

조합 생성, 테스트 데이터, 캘린더 그리드에 사용합니다.

셀프 조인 (Self JOIN)

테이블을 자기 자신과 조인합니다. 계층 데이터나 같은 테이블 내 행 비교에 유용합니다.

-- employees_v2 with manager_id column
+----+---------+------------+
| id | name    | manager_id |
+----+---------+------------+
| 1  | Alice   | NULL       |
| 2  | Bob     | 1          |
| 3  | Charlie | 1          |
| 4  | Diana   | 2          |
+----+---------+------------+

SELECT
  e.name AS employee,
  m.name AS manager
FROM employees_v2 e
LEFT JOIN employees_v2 m ON e.manager_id = m.id;

결과:

+----------+---------+
| employee | manager |
+----------+---------+
| Alice    | NULL    |
| Bob      | Alice   |
| Charlie  | Alice   |
| Diana    | Bob     |
+----------+---------+

직원-관리자 관계, 계층적 카테고리, 행 비교에 사용합니다.

어떤 조인을 사용할까? 빠른 참조

조인 유형반환 내용최적 사용 사례
INNER JOINA ∩ B일치 데이터만
LEFT JOINA + (A ∩ B)왼쪽 전체 + 일치
RIGHT JOIN(A ∩ B) + B오른쪽 전체 + 일치
FULL OUTER JOINA ∪ B두 테이블 전체
CROSS JOINA × B모든 조합 생성
Self JOIN조인 유형에 따라계층/자기참조 데이터

성능 팁

  • JOIN 열에는 항상 인덱스를 생성하세요.
  • 프로덕션에서는 SELECT *를 피하고 필요한 열만 선택하세요.
  • 가능하면 작은 테이블을 FROM 절 앞에 배치하세요.
  • EXPLAIN ANALYZE로 쿼리 실행 계획을 확인하세요.
  • 상관 서브쿼리에서는 IN보다 EXISTS를 사용하세요.
  • 표현식이나 함수에서의 조인을 피하세요 (인덱스 사용 불가).
-- Create index on JOIN column
CREATE INDEX idx_emp_dept ON employees(department_id);

-- Use EXPLAIN to check query plan
EXPLAIN ANALYZE
SELECT e.name, d.dept_name
FROM employees e
INNER JOIN departments d ON e.department_id = d.id;

흔한 실수

실수문제해결
Using SELECT *Returns unnecessary columns, wastes bandwidthSelect only needed columns
Missing index on JOIN columnFull table scan, extremely slowCREATE INDEX
NULL comparison trapNULL = NULL evaluates to FALSEUse IS NULL or COALESCE
Accidental Cartesian productMissing ON condition, row explosionAlways verify ON clause
Filtering OUTER JOIN in WHEREWHERE filter turns LEFT JOIN into INNER JOINPut conditions in ON clause
Unnecessary DISTINCTHides incorrect JOIN logicFix JOIN condition, don't add DISTINCT
Joining on functionsIndex unusable, forces full scanStore normalized data or use computed columns
-- ❌ Wrong: WHERE turns LEFT JOIN into INNER JOIN
SELECT e.name, d.dept_name
FROM employees e
LEFT JOIN departments d ON e.department_id = d.id
WHERE d.dept_name = 'Engineering';   -- filters out NULLs!

-- ✅ Correct: Put condition in ON clause
SELECT e.name, d.dept_name
FROM employees e
LEFT JOIN departments d
  ON e.department_id = d.id
  AND d.dept_name = 'Engineering';   -- preserves NULLs

자주 묻는 질문

INNER JOIN과 OUTER JOIN의 차이는?

INNER JOIN은 일치하는 행만 반환합니다. OUTER JOIN은 일치하지 않는 행도 포함하며 NULL로 채웁니다.

3개 이상의 테이블을 조인할 수 있나요?

네. 여러 JOIN을 체이닝할 수 있습니다. 실질적인 제한은 없습니다.

INNER JOIN에서 ON 절을 빠뜨리면?

대부분의 SQL에서 구문 오류가 발생합니다. 일부에서는 CROSS JOIN이 될 수 있습니다.

LEFT JOIN과 LEFT OUTER JOIN은 같나요?

네. OUTER 키워드는 선택 사항입니다. 완전히 동일합니다.

어떤 조인이 가장 빠른가요?

INNER JOIN이 일반적으로 가장 빠릅니다. 하지만 실제 성능은 인덱스, 테이블 크기, 옵티마이저에 따라 다릅니다.

NATURAL JOIN이란?

같은 이름의 열에서 자동으로 조인합니다. 열 추가 시 동작이 변경될 수 있어 프로덕션에서는 비권장입니다.

𝕏 Twitterin LinkedIn
도움이 되었나요?

최신 소식 받기

주간 개발 팁과 새 도구 알림을 받으세요.

스팸 없음. 언제든 구독 해지 가능.

Try These Related Tools

SQLSQL FormatterPSSQL to Prisma Schema{ }JSON Formatter

Related Articles

JSON vs YAML vs TOML: 어떤 설정 포맷을 사용해야 할까?

JSON, YAML, TOML 설정 포맷을 비교합니다.

Prisma 스키마 및 관계 가이드

Prisma 스키마 설계 마스터: 모델, 관계(1:1, 1:N, M:N), enum, 인덱스, 마이그레이션.