Oracle에서 여러 테이블을 결합하여 데이터를 조회하는 JOIN의 종류와 사용법을 정리합니다.
Oracle 전용 구문과 ANSI 표준 구문을 함께 설명합니다.

샘플 데이터 준비

CREATE TABLE departments (
    id    NUMBER(10) PRIMARY KEY,
    name  VARCHAR2(30) NOT NULL
);

CREATE TABLE employees (
    id       NUMBER(10) PRIMARY KEY,
    name     VARCHAR2(50) NOT NULL,
    dept_id  NUMBER(10),
    salary   NUMBER(12,2),
    CONSTRAINT fk_emp_dept FOREIGN KEY (dept_id) REFERENCES departments(id)
);

INSERT INTO departments VALUES (1, '개발팀');
INSERT INTO departments VALUES (2, '기획팀');
INSERT INTO departments VALUES (3, '디자인팀');
INSERT INTO departments VALUES (4, '인사팀');

INSERT INTO employees VALUES (1, '홍길동', 1, 5000000);
INSERT INTO employees VALUES (2, '김철수', 2, 4500000);
INSERT INTO employees VALUES (3, '이영희', 1, 5500000);
INSERT INTO employees VALUES (4, '박민수', 3, 4800000);
INSERT INTO employees VALUES (5, '최지은', NULL, 4000000);
COMMIT;

JOIN 종류 한눈에 보기

JOIN 종류 ANSI 표준 Oracle 전용 구문
INNER JOIN A INNER JOIN B ON ... WHERE A.col = B.col
LEFT OUTER JOIN A LEFT JOIN B ON ... WHERE A.col = B.col(+)
RIGHT OUTER JOIN A RIGHT JOIN B ON ... WHERE A.col(+) = B.col
FULL OUTER JOIN A FULL JOIN B ON ... Oracle 전용 구문 없음
CROSS JOIN A CROSS JOIN B FROM A, B (조건 없이)
SELF JOIN 동일 동일

ANSI 표준 구문 사용을 권장합니다. 가독성이 좋고 다른 DBMS와 호환됩니다.


INNER JOIN

-- ANSI 표준 (권장)
SELECT e.name AS 직원명, d.name AS 부서명, e.salary
FROM employees e
INNER JOIN departments d ON e.dept_id = d.id;

-- Oracle 전용 구문
SELECT e.name AS 직원명, d.name AS 부서명, e.salary
FROM employees e, departments d
WHERE e.dept_id = d.id;

LEFT OUTER JOIN

-- ANSI 표준 (권장)
SELECT e.name AS 직원명, d.name AS 부서명, e.salary
FROM employees e
LEFT JOIN departments d ON e.dept_id = d.id;

-- Oracle 전용 구문 (+)
SELECT e.name AS 직원명, d.name AS 부서명, e.salary
FROM employees e, departments d
WHERE e.dept_id = d.id(+);
-- (+)는 데이터가 없을 수 있는 쪽에 붙입니다
+--------+--------+---------+
| 직원명  | 부서명  | SALARY  |
+--------+--------+---------+
| 홍길동  | 개발팀  | 5000000 |
| 김철수  | 기획팀  | 4500000 |
| 이영희  | 개발팀  | 5500000 |
| 박민수  | 디자인팀 | 4800000 |
| 최지은  | (null) | 4000000 |
+--------+--------+---------+

매칭 안 되는 행만 조회

SELECT e.name
FROM employees e
LEFT JOIN departments d ON e.dept_id = d.id
WHERE d.id IS NULL;

RIGHT OUTER JOIN

-- ANSI 표준
SELECT e.name AS 직원명, d.name AS 부서명
FROM employees e
RIGHT JOIN departments d ON e.dept_id = d.id;

-- Oracle 전용 구문
SELECT e.name AS 직원명, d.name AS 부서명
FROM employees e, departments d
WHERE e.dept_id(+) = d.id;

FULL OUTER JOIN

양쪽 테이블의 모든 행을 반환합니다. Oracle 전용 (+) 구문으로는 표현할 수 없습니다.

SELECT e.name AS 직원명, d.name AS 부서명
FROM employees e
FULL OUTER JOIN departments d ON e.dept_id = d.id;
+--------+--------+
| 직원명  | 부서명  |
+--------+--------+
| 홍길동  | 개발팀  |
| 이영희  | 개발팀  |
| 김철수  | 기획팀  |
| 박민수  | 디자인팀 |
| 최지은  | (null) |
| (null) | 인사팀  |
+--------+--------+

CROSS JOIN

-- ANSI 표준
SELECT e.name, d.name
FROM employees e
CROSS JOIN departments d;

-- Oracle 전용 (FROM에 콤마로 나열, WHERE 없음)
SELECT e.name, d.name
FROM employees e, departments d;
-- 5명 × 4부서 = 20행

SELF JOIN

CREATE TABLE staff (
    id          NUMBER(10) PRIMARY KEY,
    name        VARCHAR2(50) NOT NULL,
    manager_id  NUMBER(10),
    CONSTRAINT fk_staff_mgr FOREIGN KEY (manager_id) REFERENCES staff(id)
);

INSERT INTO staff VALUES (1, '김대표', NULL);
INSERT INTO staff VALUES (2, '이부장', 1);
INSERT INTO staff VALUES (3, '박과장', 2);
INSERT INTO staff VALUES (4, '최사원', 3);
COMMIT;

-- 직원과 상사 이름 함께 조회
SELECT s.name AS 직원, m.name AS 상사
FROM staff s
LEFT JOIN staff m ON s.manager_id = m.id;

NATURAL JOIN / USING

Oracle에서 지원하는 간결한 JOIN 구문입니다.

-- NATURAL JOIN: 동일한 컬럼명으로 자동 매칭
-- ⚠️ 의도치 않은 컬럼 매칭 위험이 있어 실무에서는 비권장
SELECT e.name, departments.name AS dept_name
FROM employees e
NATURAL JOIN departments;

-- USING: 매칭 컬럼 명시 (컬럼명이 동일할 때)
-- dept_id 대신 양쪽 모두 id라면:
SELECT e.name, d.name
FROM employees e
JOIN departments d USING (dept_id);

다중 테이블 JOIN

CREATE TABLE projects (
    id       NUMBER(10) PRIMARY KEY,
    name     VARCHAR2(100) NOT NULL,
    dept_id  NUMBER(10),
    CONSTRAINT fk_proj_dept FOREIGN KEY (dept_id) REFERENCES departments(id)
);

-- 3개 테이블 결합
SELECT e.name AS 직원명, d.name AS 부서명, p.name AS 프로젝트명
FROM employees e
INNER JOIN departments d ON e.dept_id = d.id
INNER JOIN projects p ON d.id = p.dept_id;

-- Oracle 전용 구문
SELECT e.name AS 직원명, d.name AS 부서명, p.name AS 프로젝트명
FROM employees e, departments d, projects p
WHERE e.dept_id = d.id
  AND d.id = p.dept_id;

JOIN 사용 시 주의사항

주의사항 설명
(+) 구문 제한 FULL OUTER JOIN 불가, OR 조건과 함께 사용 불가
ANSI 표준 권장 가독성, 유지보수, 호환성 모두 우수
인덱스 확인 JOIN 컬럼에 인덱스가 없으면 성능 저하
별칭 필수 컬럼명 중복 시 반드시 테이블 별칭 사용
-- ❌ (+) 구문의 한계: OR 조건 사용 불가
SELECT * FROM employees e, departments d
WHERE e.dept_id = d.id(+) OR e.name = d.name(+);  -- 오류

-- ✅ ANSI 표준으로 해결
SELECT * FROM employees e
LEFT JOIN departments d ON e.dept_id = d.id OR e.name = d.name;

관련된 글 (oracle > lecture-oracle)