본문 바로가기
핵심 기술 튜토리얼 (Core Tech Tutorials)

주니어 분석가가 가장 많이 헷갈리는 INNER JOIN과 LEFT JOIN, 이 글 하나로 끝!

by 데이터비전 2025. 6. 16.

안녕하세요, 데이터로 비전을 찾는 '데이터비전 연구소'의 데이터비전입니다.

 

지난 포스팅 [CASE WHEN으로 데이터에 새로운 조건과 카테고리 만들기]를 통해, 우리는 데이터를 입맛에 맞게 '재창조'하는 강력한 무기를 손에 넣었습니다.

 

하지만 지금까지 우리는 `employees`라는 단 하나의 테이블 안에서만 데이터를 지지고 볶았습니다. 현실 세계의 데이터는 어떨까요? 훨씬 더 복잡합니다. 고객 정보는 `고객 테이블`에, 주문 내역은 `주문 테이블`에, 상품 정보는 `상품 테이블`에 각각 나뉘어 저장되어 있죠.

바로 여기서 실무의 가장 큰 장벽이 등장합니다.

 

"직원 정보는 `employees` 테이블에 있는데, 각 직원이 어떤 프로젝트에 참여했는지는 `projects`라는 다른 테이블에 있다면 어떻게 하죠? 두 테이블을 합쳐서 '개발팀 직원이 참여한 프로젝트 목록'을 보려면 어떻게 해야 하나요?"

 

이 질문에 대한 해답이 바로 오늘 배울 JOIN에 있습니다. JOIN은 흩어져 있는 테이블들을 '공통된 연결고리'를 이용해 하나로 합쳐주는, 관계형 데이터베이스의 심장이자 영혼입니다. SQL 면접 단골 질문 1순위인 만큼, 오늘 이 글에서 그 결정적 차이를 A to Z, 하나하나 떠먹여 드리겠습니다.


1. 흩어진 데이터를 하나로! JOIN이란?

JOIN은 두 개 이상의 테이블을 특정 조건(주로 ID와 같이 양쪽에 공통으로 존재하는 컬럼)을 기준으로 합쳐서, 마치 처음부터 하나의 테이블이었던 것처럼 보여주는 기능입니다.

 

실습을 위해 두 개의 간단한 테이블을 만들어 보겠습니다. 한 명의 직원은 프로젝트가 없는 상태입니다.

-- 직원 테이블
CREATE TABLE employees ( emp_id INT, name VARCHAR(50), department VARCHAR(50) );
INSERT INTO employees VALUES (101, '김민준', '개발팀'), (102, '이서연', '기획팀'), (103, '박도윤', '개발팀'), (104, '윤채원', '마케팅팀');

-- 프로젝트 테이블
CREATE TABLE projects ( project_id INT, project_name VARCHAR(50), emp_id INT );
INSERT INTO projects VALUES (1, '알파 프로젝트', 101), (2, '베타 프로젝트', 102), (3, '감마 프로젝트', 101), (4, '델타 프로젝트', 103);

 

우리의 목표는 이 두 테이블을 `emp_id`라는 연결고리를 사용해 합치는 것입니다.


2. 교집합만 쏙! INNER JOIN

가장 기본적이고 많이 쓰이는 INNER JOIN은 두 테이블에 모두 공통된 값이 존재하는 행들만 합쳐서 보여줍니다. 흔히 수학의 '교집합'에 비유합니다.

 

즉, `employees` 테이블과 `projects` 테이블 양쪽에 모두 `emp_id`가 존재하는 직원들의 데이터만 결과로 나옵니다.

SELECT
    A.name,
    A.department,
    B.project_name
FROM employees AS A
INNER JOIN projects AS B
    ON A.emp_id = B.emp_id;

 

❗ 잠깐, AS A, ON?
- AS A, AS B: 테이블 이름이 길 때, 각각 A와 B라는 별명을 붙여주어 쿼리를 간결하게 만듭니다.
- ON: 두 테이블을 어떤 조건(연결고리)으로 합칠지 지정해주는 부분입니다. 여기서는 'A 테이블의 emp_id와 B 테이블의 emp_id가 같은 것'을 기준으로 삼았습니다.

 

결과를 확인해 보세요. '윤채원' 직원은 `projects` 테이블에 참여한 프로젝트가 없기 때문에, 교집합에 해당하지 않아 결과에서 제외되었습니다. 이것이 `INNER JOIN`의 가장 중요한 특징입니다.

 

SQL JOIN 사용법: INNER JOIN, LEFT JOIN


3. 왼쪽은 다 보여줘! LEFT JOIN

LEFT JOIN은 이름 그대로, 왼쪽(먼저 나온) 테이블의 데이터는 모두 보여주고, 그 다음 오른쪽 테이블에서 `ON` 조건에 맞는 데이터를 가져와 붙여줍니다. 만약 오른쪽 테이블에 짝이 되는 데이터가 없으면, 그 부분은 `NULL`(빈 값)로 채워집니다.

이번에는 `INNER JOIN`을 `LEFT JOIN`으로만 바꿔서 실행해 보겠습니다.

SELECT
    A.name,
    A.department,
    B.project_name
FROM employees AS A
LEFT JOIN projects AS B
    ON A.emp_id = B.emp_id;

 

결과가 어떻게 달라졌나요? 이번에는 프로젝트에 참여하지 않은 '윤채원' 직원도 결과에 포함되었습니다! 대신, 그녀의 `project_name` 컬럼은 `NULL`로 표시됩니다. 왼쪽 `employees` 테이블을 기준으로 모든 직원을 일단 다 보여주고, 짝이 있는 프로젝트만 옆에 붙여준 것이죠.

 

이것이 바로 `INNER JOIN`과 `LEFT JOIN`의 결정적인 차이입니다.

  • INNER JOIN: 양쪽 테이블에 모두 짝이 있는 데이터만 보고 싶을 때 (예: 주문 내역이 있는 고객만 조회)
  • LEFT JOIN: 왼쪽 기준 테이블의 모든 데이터를 보면서, 오른쪽에 관련된 정보가 있다면 함께 보고 싶을 때 (예: 전체 고객 목록을 보되, 주문 내역이 있다면 함께 표시)

마무리하며: 이제 당신은 데이터를 연결할 수 있습니다

오늘은 관계형 데이터베이스의 핵심, JOIN을 함께했습니다. `INNER JOIN`과 `LEFT JOIN`의 차이점, 이제 명확히 이해되셨나요? 어떤 JOIN을 사용하느냐에 따라 분석 결과가 완전히 달라질 수 있기 때문에, 그 차이를 명확히 아는 것이 정말 중요합니다.

 

이제 여러분은 여러 곳에 흩어져 있는 데이터를 연결하여 훨씬 더 풍부하고 깊이 있는 분석을 할 수 있는 강력한 무기를 손에 넣었습니다.

그런데 여기서 또 다른 실무적인 문제가 생깁니다.

 

"JOIN을 하고, CASE WHEN도 쓰고, GROUP BY도 하다 보니 쿼리가 너무 길고 복잡해졌어요. `SELECT ... FROM (SELECT ... FROM (SELECT ...))` 이런 식으로 괄호가 너무 많아져서 어디가 어디인지 알아볼 수가 없어요. 좀 더 깔끔하게 정리하는 방법은 없을까요?"

 

다음 포스팅에서는 이처럼 복잡한 쿼리를 마치 글을 쓰듯 깔끔하게 정리해주는 서브쿼리(Subquery)와, 그보다 더 진화한 형태인 WITH 구문(CTE)에 대해 알아보겠습니다. 코드의 가독성을 높이고 유지보수를 쉽게 만드는, 모든 분석가의 필수 교양과목이니 절대 놓치지 마세요!

 

오늘 배운 JOIN, 어떤 상황에 어떤 JOIN을 써야 할지 아직 헷갈리는 부분이 있나요? 댓글로 남겨주시면 연구소장이 직접 답변해 드리겠습니다!