ANSI JOIN 종류별 상세 설명과 활용 예제

INNER JOIN, LEFT OUTER JOIN 2개만 알면 거의 모든 요구 사항이 커버가 됩니다.

하지만 가끔 저 2개만 가지고는 안될 때가 있습니다. 실무에는 정말 다양한 요구 사항이 있기 때문에 모든 JOIN 유형을 다 알아두면 분명 도움이 될 일이 있을 겁니다.

샘플 테이블 및 데이터 생성 스크립트와 SQL은 PostgreSQL 기준으로 작성되었지만 대부분의 DB에서 그대로 사용이 가능합니다.


샘플 테이블 및 데이터 생성 스크립트

CREATE TABLE departments (

department_id INT PRIMARY KEY, /* Oracle이면 INT 대신 NUMBER */

department_name VARCHAR(50) NOT NULL /* Oracle이면 VARCHAR 대신 VARCHAR2 */

);


CREATE TABLE employees (

employee_id INT PRIMARY KEY, /* Oracle이면 INT 대신 NUMBER */

employee_name VARCHAR(50) NOT NULL, /* Oracle이면 VARCHAR 대신 VARCHAR2 */

department_id INT, /* Oracle이면 INT 대신 NUMBER */

manager_id INT, /* Oracle이면 INT 대신 NUMBER */

FOREIGN KEY (department_id) REFERENCES departments(department_id)

);


INSERT INTO departments (department_id, department_name) VALUES (10, '개발팀');

INSERT INTO departments (department_id, department_name) VALUES (20, '마케팅팀');

INSERT INTO departments (department_id, department_name) VALUES (30, '인사팀');

INSERT INTO departments (department_id, department_name) VALUES (40, '재무팀');


INSERT INTO employees (employee_id, employee_name, department_id, manager_id)

VALUES (1, '김철수', 10, NULL);

INSERT INTO employees (employee_id, employee_name, department_id, manager_id)

VALUES (2, '이영희', 20, 1);

INSERT INTO employees (employee_id, employee_name, department_id, manager_id)

VALUES (3, '박지민', 10, 1);

INSERT INTO employees (employee_id, employee_name, department_id, manager_id)

VALUES (4, '최동욱', 30, 2);

INSERT INTO employees (employee_id, employee_name, department_id, manager_id)

VALUES (5, '정수민', NULL, 3);

INNER JOIN

SELECT e.employee_name, d.department_name

FROM employees e

INNER JOIN departments d

ON e.department_id = d.department_id;


employee_name | department_name

--------------|----------------

김철수        | 개발팀

이영희        | 마케팅팀

박지민        | 개발팀

최동욱        | 인사팀


두 테이블 모두 JOIN 조건을 만족하는 행을 반환합니다.

"INNER JOIN"을 "JOIN"으로 줄여서 쓸 수 있습니다.


LEFT OUTER JOIN

SELECT e.employee_name, d.department_name

FROM employees e

LEFT OUTER JOIN departments d

ON e.department_id = d.department_id;


employee_name | department_name

--------------|----------------

김철수        | 개발팀

이영희        | 마케팅팀

박지민        | 개발팀

최동욱        | 인사팀

정수민        | NULL


왼쪽 테이블의 모든 행과 오른쪽 테이블의 JOIN 조건을 만족하는 행을 반환합니다.

"LEFT OUTER JOIN"을 "LEFT JOIN"으로 줄여서 쓸 수 있습니다.

두 테이블 모두 데이터가 100% 있는 구조일 경우는 무조건 INNER JOIN을 써야 합니다. 이런 경우에도 LEFT OUTER JOIN을 사용하는 경우가 실무에서 꽤 많이 보이는데 이럴 경우 성능이 저하될 수 있고, 쿼리를 보는 개발자도 혼란스러워 할 수 있습니다. ('두 테이블 다 데이터가 있을 것 같은데 왜 LEFT OUTER JOIN을 해 놨지?')


RIGHT OUTER JOIN

SELECT e.employee_name, d.department_name

FROM employees e

RIGHT OUTER JOIN departments d

ON e.department_id = d.department_id;


employee_name | department_name

--------------|----------------

김철수        | 개발팀

박지민        | 개발팀

이영희        | 마케팅팀

최동욱        | 인사팀

NULL          | 재무팀


LEFT OUTER JOIN과 반대로 오른쪽 테이블의 모든 행과 왼쪽 테이블의 JOIN 조건을 만족하는 행을 반환합니다.

LEFT OUTER JOIN을 쓰는 게 일반적이므로 되도록 안 쓰는 게 좋습니다. LEFT OUTER JOIN은 모든 개발자가 익숙하지만 RIGHT OUTER JOIN을 보면 별거 아닌데도 익숙하지 않기 때문에 생각을 한번 더 해야 합니다.

* 극히 드물게 유용하게 쓰이는 경우도 있다고는 합니다. (예: 이미 작성된 복잡한 서브쿼리의 출력 구조를 유지하면서 오른쪽 테이블을 기준으로 조인해야 할 때)


FULL OUTER JOIN

SELECT e.employee_name, d.department_name

FROM employees e

FULL OUTER JOIN departments d

ON e.department_id = d.department_id;


employee_name | department_name

--------------|----------------

김철수        | 개발팀

이영희        | 마케팅팀

박지민        | 개발팀

최동욱        | 인사팀

정수민        | NULL

NULL          | 재무팀


양쪽 테이블의 모든 행을 반환합니다. 일치하는 행이 없는 경우는 NULL로 채워집니다.

MySQL에서는 지원하지 않아서 LEFT OUTER JOIN UNION RIGHT OUTER JOIN 형태로 사용해야 합니다. ANSI SQL이라고 해서 꼭 모든 DB에서 지원하는 것은 아닌 것 같습니다.

SELECT * FROM TableA LEFT JOIN TableB ON ...

UNION

SELECT * FROM TableA RIGHT JOIN TableB ON ...;


CROSS JOIN

SELECT e.employee_name, d.department_name

FROM employees e

CROSS JOIN departments d

ORDER BY e.employee_name;


employee_name | department_name

--------------|----------------

김철수        | 개발팀

김철수        | 마케팅팀

김철수        | 인사팀

김철수        | 재무팀

이영희        | 개발팀

이영희        | 마케팅팀

이영희        | 인사팀

이영희        | 재무팀

...


첫 번째 테이블 각 행이 두 번째 테이블의 모든 행과 결합됩니다.

모든 가능한 조합을 조회해야 할 때 사용됩니다.


SELF JOIN

SELECT e.employee_name AS employee, m.employee_name AS manager

FROM employees e

LEFT OUTER JOIN employees m

ON e.manager_id = m.employee_id;


employee     | manager

-------------|----------

김철수       | NULL

이영희       | 김철수

박지민       | 김철수

최동욱       | 이영희

정수민       | 박지민


특별한 문법이 있는 건 아니고, 개념적으로만 존재하는 JOIN 입니다. 동일 테이블 내 레코드 간 관계 분석이 필요할 때 사용됩니다.


NATURAL JOIN

SELECT e.employee_name, d.department_name

FROM employees e

NATURAL JOIN departments d;


employee_name | department_name

--------------|----------------

김철수        | 개발팀

이영희        | 마케팅팀

박지민        | 개발팀

최동욱        | 인사팀


두 테이블에서 이름이 같은 모든 칼럼의 값이 같으면 자동으로 JOIN 합니다. JOIN 조건을 일일이 입력하지 않아도 되니까 편해 보일 수도 있긴 한데 일반적으로 사용이 권장되지는 않습니다. 아주 간단한 구조이고, 테이블 수정도 절대 없을 거라면 사용해도 되지만 그럴 일은 그리 많지 않겠죠?


주요 DB ANSI JOIN 유형 지원 현황

MySQLPostgreSQLOracleSQL ServerSQLite
INNER JOIN
LEFT OUTER JOIN
RIGHT OUTER JOIN
FULL OUTER JOIN
CROSS JOIN
SELF JOIN
NATURAL JOIN⚠️⚠️⚠️⚠️⚠️


맺음말

자주 사용하는 INNER JOIN, LEFT OUTER JOIN 외의 JOIN 유형도 미리 숙지하고 있으면 많은 도움이 될 것입니다. 

위의 표에 보이는 것처럼 ANSI JOIN이라고 해서 모든 DB에서 지원하는 건 아니라는 것도 꼭 알아두는 게 좋습니다.

각 DB에서만 적용되는 고유한 JOIN 문법보다는 대부분의 DB에서 공통적으로 사용되는 ANSI SQL 표준 문법을 적극적으로 사용하도록 합시다.



댓글

이 블로그의 인기 게시물

Spring Boot Initializr 사용법 상세 가이드

SQL 테이블 ALIAS 규칙: 가장 효과적인 방법과 장단점 비교

PostgreSQL 로컬 PC(윈도우)에 설치하기 - 17.4버전 기준