

NATURAL JOIN, INNER JOIN, OUTER JOIN, USING 절, ON 절 등 다양한 형태로 발전되었다.ANSI/ISO SQL 표준에서 정의한 FROM 절의 JOIN 형태는 다음과 같다.
SELECT * FROM table1 INNER JOIN table2 ON table1.id = table2.id;SELECT * FROM table1 NATURAL JOIN table2;SELECT * FROM table1 JOIN table2 USING (common_column);SELECT * FROM table1 JOIN table2 ON table1.id = table2.id;SELECT * FROM table1 CROSS JOIN table2;SELECT * FROM table1 LEFT OUTER JOIN table2 ON table1.id = table2.id;INNER JOIN은 두 테이블 간의 조인 조건에서 동일한 값을 가진 행만 반환한다. 이는 OUTER JOIN과 대조적으로 내부 조인이라고 하며, 조인 조건을 만족하는 데이터만을 반환하는 것이 특징이다. INNER JOIN의 사용법과 특징을 설명하면 다음과 같다.
-- WHERE 절에서의 INNER JOIN
-- 이 방식은 WHERE 절에서 조인 조건을 사용하여 데이터를 결합한다.
SELECT EMP.DEPTNO, EMPNO, ENAME, DNAME
FROM EMP, DEPT
WHERE EMP.DEPTNO = DEPT.DEPTNO;
-- FROM 절에서의 INNER JOIN
-- 이 방식은 FROM 절에서 조인 조건을 명시적으로 정의한다.
-- INNER JOIN은 기본 옵션이므로 생략이 가능하다
SELECT EMP.DEPTNO, EMPNO, ENAME, DNAME
FROM EMP INNER JOIN DEPT ON EMP.DEPTNO = DEPT.DEPTNO;
SELECT EMP.DEPTNO, EMPNO, ENAME, DNAME
FROM EMP JOIN DEPT ON EMP.DEPTNO = DEPT.DEPTNO;
실행 결과는 다음과 같다.
DEPTNO EMPNO ENAME DNAME
------ ----- ------ ---------
20 7369 SMITH RESEARCH
30 7499 ALLEN SALES
30 7521 WARD SALES
20 7566 JONES RESEARCH
30 7654 MARTIN SALES
30 7698 BLAKE SALES
10 7782 CLARK ACCOUNTING
20 7788 SCOTT RESEARCH
10 7839 KING ACCOUNTING
30 7844 TURNER SALES
20 7876 ADAMS RESEARCH
30 7900 JAMES SALES
20 7902 FORD RESEARCH
10 7934 MILLER ACCOUNTING
EMP 테이블
| EMPNO | ENAME | JOB | MGR | HIREDATE | SAL | COMM | DEPTNO |
|---|---|---|---|---|---|---|---|
| 7369 | SMITH | CLERK | 7902 | 1980-12-17 | 800 | NULL | 20 |
| 7499 | ALLEN | SALESMAN | 7698 | 1981-02-20 | 1600 | 300 | 30 |
| 7521 | WARD | SALESMAN | 7698 | 1981-02-22 | 1250 | 500 | 30 |
| 7566 | JONES | MANAGER | 7839 | 1981-04-02 | 2975 | NULL | 20 |
| 7654 | MARTIN | SALESMAN | 7698 | 1981-09-28 | 1250 | 1400 | 30 |
| 7698 | BLAKE | MANAGER | 7839 | 1981-05-01 | 2850 | NULL | 30 |
| 7782 | CLARK | MANAGER | 7839 | 1981-06-09 | 2450 | NULL | 10 |
| 7788 | SCOTT | ANALYST | 7566 | 1982-12-09 | 3000 | NULL | 20 |
| 7839 | KING | PRESIDENT | NULL | 1981-11-17 | 5000 | NULL | 10 |
| 7844 | TURNER | SALESMAN | 7698 | 1981-09-08 | 1500 | 0 | 30 |
| 7876 | ADAMS | CLERK | 7788 | 1983-01-12 | 1100 | NULL | 20 |
| 7900 | JAMES | CLERK | 7698 | 1981-12-03 | 950 | NULL | 30 |
| 7902 | FORD | ANALYST | 7566 | 1981-12-03 | 3000 | NULL | 20 |
| 7934 | MILLER | CLERK | 7782 | 1982-01-23 | 1300 | NULL | 10 |
DEPT 테이블의 데이터
| DEPTNO | DNAME | LOC |
|---|---|---|
| 10 | ACCOUNTING | NEW YORK |
| 20 | RESEARCH | DALLAS |
| 30 | SALES | CHICAGO |
| 40 | OPERATIONS | BOSTON |
NATURAL JOIN은 두 테이블 간의 동일한 이름을 가진 모든 칼럼에 대해 EQUI JOIN을 수행한다. NATURAL JOIN은 동일한 칼럼명과 데이터 유형을 가진 칼럼에 대해 자동으로 조인 조건을 생성한다.
-- NATURAL JOIN 사용 예제
SELECT DEPTNO, EMPNO, ENAME, DNAME
FROM EMP NATURAL JOIN DEPT;
DEPTNO EMPNO ENAME DNAME
------ ----- ------ ---------
20 7369 SMITH RESEARCH
30 7499 ALLEN SALES
30 7521 WARD SALES
20 7566 JONES RESEARCH
30 7654 MARTIN SALES
30 7698 BLAKE SALES
10 7782 CLARK ACCOUNTING
20 7788 SCOTT RESEARCH
10 7839 KING ACCOUNTING
30 7844 TURNER SALES
20 7876 ADAMS RESEARCH
30 7900 JAMES SALES
20 7902 FORD RESEARCH
10 7934 MILLER ACCOUNTING
NATURAL JOIN은 동일한 칼럼명을 가진 칼럼에 대해 자동으로 조인 조건을 생성하며, 동일한 이름의 칼럼을 하나로 처리한다.
SELECT * FROM EMP NATURAL JOIN DEPT;
INNER JOIN은 명시적으로 조인 조건을 정의하며, 동일한 이름의 칼럼을 별개의 칼럼으로 표시한다.
SELECT * FROM EMP INNER JOIN DEPT ON EMP.DEPTNO = DEPT.DEPTNO;
CREATE TABLE DEPT_TEMP AS SELECT * FROM DEPT; -- Oracle
SELECT * INTO DEPT_TEMP FROM DEPT; -- SQL Server
UPDATE DEPT_TEMP SET DNAME = 'R&D' WHERE DNAME = 'RESEARCH';
UPDATE DEPT_TEMP SET DNAME = 'MARKETING' WHERE DNAME = 'SALES';
SELECT * FROM DEPT_TEMP;
DEPT_TEMP 테이블 데이터는 아래와 같다.
DEPTNO DNAME LOC
------ ---------- ---------
10 ACCOUNTING NEW YORK
20 R&D DALLAS
30 MARKETING CHICAGO
40 OPERATIONS BOSTON
SELECT * FROM DEPT NATURAL JOIN DEPT_TEMP;
-- 실행 결과
DEPTNO DNAME LOC
------ ---------- ---------
10 ACCOUNTING NEW YORK
40 OPERATIONS BOSTON
부서번호 20, 30의 데이터는 DNAME이 변경되어 제외되었다.
SELECT * FROM DEPT JOIN DEPT_TEMP
ON DEPT.DEPTNO = DEPT_TEMP.DEPTNO
AND DEPT.DNAME = DEPT_TEMP.DNAME
AND DEPT.LOC = DEPT_TEMP.LOC;
-- 실행 결과는 아래와 같다.
DEPTNO DNAME LOC DEPTNO DNAME LOC
------ ---------- --------- ------ ---------- ---------
10 ACCOUNTING NEW YORK 10 ACCOUNTING NEW YORK
40 OPERATIONS BOSTON 40 OPERATIONS BOSTON
USING 조건절은 NATURAL JOIN과 유사하지만, 선택한 특정 칼럼에 대해서만 EQUI JOIN을 수행할 수 있는 기능을 제공한다. 이 기능은 SQL Server에서는 지원되지 않는다.
SELECT * FROM DEPT JOIN DEPT_TEMP USING (DEPTNO);
-- 실행 결과
DEPTNO DNAME LOC DNAME LOC
------ ---------- --------- ---------- ---------
10 ACCOUNTING NEW YORK ACCOUNTING NEW YORK
20 RESEARCH DALLAS R&D DALLAS
30 SALES CHICAGO MARKETING CHICAGO
40 OPERATIONS BOSTON OPERATIONS BOSTON
-- 잘못된 예제
SELECT DEPT.DEPTNO, DEPT.DNAME, DEPT.LOC, DEPT_TEMP.DNAME, DEPT_TEMP.LOC
FROM DEPT JOIN DEPT_TEMP USING (DEPTNO);
-- USING 절을 사용할 때는 이러한 식별자를 포함한 열 지정이 허용되지 않는다.
-- ERROR: USING 절의 열 부분은 식별자를 가질 수 없다.
-- 올바른 예제
SELECT DEPTNO, DEPT.DNAME, DEPT.LOC, DEPT_TEMP.DNAME, DEPT_TEMP.LOC
FROM DEPT JOIN DEPT_TEMP USING (DEPTNO);
-- USING 절을 사용할 때는 이렇게 열 이름만 지정해야 한다.
-- 실행 결과
DEPTNO DNAME LOC DNAME LOC
------ ---------- --------- ---------- ---------
10 ACCOUNTING NEW YORK ACCOUNTING NEW YORK
20 RESEARCH DALLAS R&D DALLAS
30 SALES CHICAGO MARKETING CHICAGO
40 OPERATIONS BOSTON OPERATIONS BOSTON
SELECT * FROM DEPT JOIN DEPT_TEMP USING (DNAME);
-- 실행 결과
DNAME DEPTNO LOC DEPTNO LOC
---------- ------ --------- ------ ---------
ACCOUNTING 10 NEW YORK 10 NEW YORK
OPERATIONS 40 BOSTON 40 BOSTON
DNAME이 바뀐 부서번호 20, 30은 제외되었다.
SELECT * FROM DEPT JOIN DEPT_TEMP USING (LOC, DEPTNO);
LOC DEPTNO DNAME DNAME
-------- ------ ---------- ----------
NEW YORK 10 ACCOUNTING ACCOUNTING
DALLAS 20 RESEARCH R&D
CHICAGO 30 SALES MARKETING
BOSTON 40 OPERATIONS OPERATIONS
SELECT * FROM DEPT JOIN DEPT_TEMP USING (DEPTNO, DNAME);
DEPTNO DNAME LOC LOC
------ ---------- --------- ---------
10 ACCOUNTING NEW YORK NEW YORK
40 OPERATIONS BOSTON BOSTON
DNAME이 바뀐 부서번호 20, 30은 제외되었다.
ON 조건절은 JOIN 서술부로, JOIN 조건을 명확하게 지정하여 이해하기 쉽게 만드는 데 사용된다. ON 조건절을 사용하면 칼럼명이 다르더라도 JOIN 조건을 설정할 수 있다. 이는 JOIN 조건과 비 JOIN 조건을 분리하는 데 도움이 된다.
다음은 사원 테이블과 부서 테이블에서 사원 번호, 사원 이름, 소속 부서 코드, 소속 부서 이름 출력하는 예제이다.
SELECT E.EMPNO, E.ENAME, E.DEPTNO, D.DNAME
FROM EMP E JOIN DEPT D ON (E.DEPTNO = D.DEPTNO);
-- 실행 결과
EMPNO ENAME DEPTNO DNAME
----- ------- ------ -----------
7369 SMITH 20 RESEARCH
7499 ALLEN 30 SALES
7521 WARD 30 SALES
7566 JONES 20 RESEARCH
7654 MARTIN 30 SALES
7698 BLAKE 30 SALES
7782 CLARK 10 ACCOUNTING
7788 SCOTT 20 RESEARCH
7839 KING 10 ACCOUNTING
7844 TURNER 30 SALES
7876 ADAMS 20 RESEARCH
7900 JAMES 30 SALES
7902 FORD 20 RESEARCH
7934 MILLER 10 ACCOUNTING
ON 조건절과 WHERE 절을 함께 사용하면 JOIN 조건과 필터 조건을 분리할 수 있다.
다음은 부서코드 30인 부서의 소속 사원 이름, 소속 부서 코드, 부서 코드, 부서 이름을 선택하는 예제이다.
SELECT E.ENAME, E.DEPTNO, D.DEPTNO, D.DNAME
FROM EMP E JOIN DEPT D ON (E.DEPTNO = D.DEPTNO)
WHERE E.DEPTNO = 30;
-- 실행 결과
ENAME DEPTNO DEPTNO DNAME
------ ------ ------ ------
ALLEN 30 30 SALES
WARD 30 30 SALES
MARTIN 30 30 SALES
BLAKE 30 30 SALES
TURNER 30 30 SALES
JAMES 30 30 SALES
JOIN 조건 외에도 데이터 검증 조건을 추가할 수 있지만, 데이터 검색 조건은 WHERE 절을 사용하는 것이 권장된다.
다음은 매니저 사원번호가 7698번인 사원들의 이름, 소속 부서 코드, 부서 이름을 선택하는 예제이다.
SELECT E.ENAME, E.MGR, D.DEPTNO, D.DNAME
FROM EMP E JOIN DEPT D ON (E.DEPTNO = D.DEPTNO AND E.MGR = 7698);
또는
SELECT E.ENAME, E.MGR, D.DEPTNO, D.DNAME
FROM EMP E JOIN DEPT D ON (E.DEPTNO = D.DEPTNO)
WHERE E.MGR = 7698;
-- 실행 결과
ENAME MGR DEPTNO DNAME
------ ---- ------ ------
ALLEN 7698 30 SALES
WARD 7698 30 SALES
MARTIN 7698 30 SALES
TURNER 7698 30 SALES
JAMES 7698 30 SALES
여러 테이블을 JOIN할 때도 ON 조건절을 사용하여 명확하게 JOIN 조건을 정의할 수 있다.
사원과 DEPT 테이블의 소속 부서명, DEPT_TEMP 테이블의 바뀐 부서명 정보 출력하는 쿼리문은 다음과 같다.
SELECT E.EMPNO, D.DEPTNO, D.DNAME, T.DNAME AS New_DNAME
FROM EMP E JOIN DEPT D ON (E.DEPTNO = D.DEPTNO)
JOIN DEPT_TEMP T ON (E.DEPTNO = T.DEPTNO);
또는
SELECT E.EMPNO, D.DEPTNO, D.DNAME, T.DNAME AS New_DNAME
FROM EMP E, DEPT D, DEPT_TEMP T
WHERE E.DEPTNO = D.DEPTNO AND E.DEPTNO = T.DEPTNO;
-- 실행 결과
EMPNO DEPTNO DNAME NEW_DNAME
----- ------ --------- ----------
7369 20 RESEARCH R&D
7499 30 SALES MARKETING
7521 30 SALES MARKETING
7566 20 RESEARCH R&D
7654 30 SALES MARKETING
7698 30 SALES MARKETING
7782 10 ACCOUNTING ACCOUNTING
7788 20 RESEARCH R&D
7839 10 ACCOUNTING ACCOUNTING
7844 30 SALES MARKETING
7876 20 RESEARCH R&D
7900 30 SALES MARKETING
7902 20 RESEARCH R&D
7934 10 ACCOUNTING ACCOUNTING
위 문제의 다중 테이블의 JOIN의 순서는, 첫 번째 JOIN은 EMP와 DEPT 사이에서 이루어지고, 두 번째 JOIN은 그 결과와 DEPT_TEMP 사이에서 이루어진다.
-- 첫 번째 조인
SELECT E.EMPNO, D.DEPTNO, D.DNAME
FROM EMP E
JOIN DEPT D ON (E.DEPTNO = D.DEPTNO)
-- 두 번째 조인
SELECT E.EMPNO, D.DEPTNO, D.DNAME, T.DNAME AS New_DNAME
FROM (
SELECT E.EMPNO, D.DEPTNO, D.DNAME
FROM EMP E
JOIN DEPT D ON (E.DEPTNO = D.DEPTNO)
) AS E_D
JOIN DEPT_TEMP T ON (E_D.DEPTNO = T.DEPTNO)
GK 포지션의 선수별 연고지명, 팀명, 구장명을 출력하는 쿼리문은 다음과 같다.
SELECT P.PLAYER_NAME AS 선수명, P.POSITION AS 포지션, T.REGION_NAME AS 연고지명, T.TEAM_NAME AS 팀명, S.STADIUM_NAME AS 구장명
FROM PLAYER P JOIN TEAM T ON P.TEAM_ID = T.TEAM_ID
JOIN STADIUM S ON T.STADIUM_ID = S.STADIUM_ID
WHERE P.POSITION = 'GK'
ORDER BY 선수명;
또는
SELECT P.PLAYER_NAME AS 선수명, P.POSITION AS 포지션, T.REGION_NAME AS 연고지명, T.TEAM_NAME AS 팀명, S.STADIUM_NAME AS 구장명
FROM PLAYER P, TEAM T, STADIUM S
WHERE P.TEAM_ID = T.TEAM_ID
AND T.STADIUM_ID = S.STADIUM_ID
AND P.POSITION = 'GK'
ORDER BY 선수명;
-- 실행 결과
선수명 포지션 연고지명 팀명 구장명
----- ---- ------ -------- ----------
강성일 GK 대전 시티즌 대전월드컵경기장
권정혁 GK 울산 울산현대 울산문수경기장
권찬수 GK 성남 일화천마 성남종합운동장
김대희 GK 포항 스틸러스 포항스틸야드
김승준 GK 대전 시티즌 대전월드컵경기장
김용발 GK 전북 현대모터스 전주월드컵경기장
김운재 GK 수원 삼성블루윙즈 수원월드컵경기장
김정래 GK 전남 드래곤즈 광양전용경기장
김준호 GK 포항 스틸러스 포항스틸야드
김창민 GK 전북 현대모터스 전주월드컵경기장
김충호 GK 인천 유나이티드 인천월드컵경기장
남현우 GK 인천 유나이티드 인천월드컵경기장
박유석 GK 부산 아이파크 부산아시아드경기장
CROSS JOIN은 테이블 간의 조인 조건이 없는 경우 발생할 수 있는 모든 데이터 조합을 반환하는 JOIN 방식이다. E.F. Codd 박사가 언급한 일반 집합 연산자의 PRODUCT 개념에 해당하며, 두 테이블 간의 모든 행을 결합하여 조합을 생성한다. 이를 CARTESIAN PRODUCT 또는 CROSS PRODUCT라고도 한다. 결과는 두 테이블의 행 수를 곱한 만큼의 데이터 조합이 발생한다.
(M*N) 만큼의 행을 포함한다.사원 번호와 사원 이름, 소속부서 코드와 소속부서 이름을 출력하는 쿼리문은 아래와 같다.
SELECT ENAME, DNAME
FROM EMP CROSS JOIN DEPT
ORDER BY ENAME;
-- 실행 결과
ENAME DNAME
-------- ---------
ADAMS SALES
ADAMS RESEARCH
ADAMS OPERATIONS
ADAMS ACCOUNTING
ALLEN OPERATIONS
ALLEN RESEARCH
ALLEN ACCOUNTING
ALLEN SALES
BLAKE SALES
BLAKE OPERATIONS
BLAKE RESEARCH
BLAKE ACCOUNTING
CLARK SALES
CLARK RESEARCH
CLARK OPERATIONS
CLARK ACCOUNTING
CROSS JOIN에서 WHERE 절을 사용하여 조건을 추가하면, INNER JOIN과 동일한 결과가 되므로 CROSS JOIN의 의미가 없어진다.
WHERE 절을 사용한 CROSS JOIN은 다음과 같다.
SELECT ENAME, DNAME
FROM EMP CROSS JOIN DEPT
WHERE EMP.DEPTNO = DEPT.DEPTNO;
또는
SELECT ENAME, DNAME
FROM EMP INNER JOIN DEPT
ON EMP.DEPTNO = DEPT.DEPTNO;
-- 실행 결과
ENAME DNAME
------- ---------
SMITH RESEARCH
ALLEN SALES
WARD SALES
JONES RESEARCH
MARTIN SALES
BLAKE SALES
CLARK ACCOUNTING
SCOTT RESEARCH
KING ACCOUNTING
TURNER SALES
ADAMS RESEARCH
JAMES SALES
FORD RESEARCH
MILLER ACCOUNTING
다음과 같이, JOIN문이 나타나지 않은 SELECT문은 카타시안 곱이 나타난다.
SELECT ENAME, DNAME
FROM EMP, DEPT
ORDER BY ENAME;
CROSS JOIN은 일반적인 데이터 모델링에서는 많이 사용되지 않지만, 특정 상황에서는 유용하게 사용할 수 있다.
- CROSS JOIN은 두 테이블 간의 모든 가능한 데이터 조합을 반환하는 JOIN 방식이다.
- 일반적인 데이터 모델에서는 자주 사용되지 않지만, 특정 상황에서 유용할 수 있다.
- WHERE 절을 사용하여 조건을 추가하면 INNER JOIN과 동일한 결과가 되므로 주의해야 한다.
OUTER JOIN은 INNER JOIN과 달리 JOIN 조건에서 동일한 값이 없는 행도 반환할 때 사용할 수 있다. 이를 통해, 두 테이블 간의 관계에서 일치하지 않는 데이터도 포함할 수 있다. OUTER JOIN에는 LEFT OUTER JOIN, RIGHT OUTER JOIN, FULL OUTER JOIN이 있다.
LEFT OUTER JOIN은 기준이 되는 좌측 테이블의 모든 데이터를 반환하며, 우측 테이블에서 일치하는 데이터가 없으면 NULL을 반환한다.
STADIUM과 TEAM 테이블을 JOIN하여 홈팀이 없는 경기장 정보도 출력하는 쿼리문은 아래와 같다.
SELECT STADIUM_NAME, STADIUM.STADIUM_ID, SEAT_COUNT, HOMETEAM_ID, TEAM_NAME
FROM STADIUM
LEFT OUTER JOIN TEAM ON STADIUM.HOMETEAM_ID = TEAM.TEAM_ID
ORDER BY HOMETEAM_ID;
혹은, 아래와 같이 OUTER키워드를 생략할 수 있다.
SELECT STADIUM_NAME, STADIUM.STADIUM_ID, SEAT_COUNT, HOMETEAM_ID, TEAM_NAME
FROM STADIUM
LEFT JOIN TEAM ON STADIUM.HOMETEAM_ID = TEAM.TEAM_ID
ORDER BY HOMETEAM_ID;
STADIUM_NAME STADIUM_ID SEAT_COUNT HOMETEAM_ID TEAM_NAME
------------------- ---------- ---------- ----------- ----------
울산문수경기장 C04 46102 K01 울산현대
수원월드컵경기장 B04 50000 K02 삼성블루윙즈
포항스틸야드 C06 25000 K03 스틸러스
인천월드컵경기장 B01 35000 K04 유나이티드
전주월드컵경기장 D03 28000 K05 현대모터스
부산아시아드경기장 C02 30000 K06 아이파크
광양전용경기장 D01 20009 K07 드래곤즈
성남종합운동장 B02 27000 K08 일화천마
서울월드컵경기장 B05 66806 K09 FC서울
대전월드컵경기장 D02 41000 K10 시티즌
창원종합운동장 C05 27085 K11 경남FC
광주월드컵경기장 A02 40245 K12 광주상무
강릉종합경기장 A03 33000 K13 강원FC
제주월드컵경기장 A04 42256 K14 제주유나이티드FC
대구월드컵경기장 A05 66422 K15 대구FC
안양경기장 F05 20000 NULL NULL
마산경기장 F04 20000 NULL NULL
일산경기장 F03 20000 NULL NULL
부산시민경기장 F02 30000 NULL NULL
대구시민경기장 F01 30000 NULL NULL
홈팀이 없는 경기장도 포함되어 있다.
RIGHT OUTER JOIN은 기준이 되는 우측 테이블의 모든 데이터를 반환하며, 좌측 테이블에서 일치하는 데이터가 없으면 NULL을 반환한다.
DEPT와 EMP 테이블을 JOIN하여 사원이 없는 부서 정보도 출력하는 쿼리문은 아래와 같다.
SELECT E.ENAME, D.DEPTNO, D.DNAME
FROM EMP E
RIGHT OUTER JOIN DEPT D ON E.DEPTNO = D.DEPTNO;
OUTER키워드를 생략하면 아래와 같다.
SELECT E.ENAME, D.DEPTNO, D.DNAME, D.LOC
FROM EMP E
RIGHT JOIN DEPT D ON E.DEPTNO = D.DEPTNO;
-- 실행 결과
ENAME DEPTNO DNAME LOC
-------- ------ ----------- --------
CLARK 10 ACCOUNTING NEW YORK
KING 10 ACCOUNTING NEW YORK
MILLER 10 ACCOUNTING NEW YORK
JONES 20 RESEARCH DALLAS
FORD 20 RESEARCH DALLAS
ADAMS 20 RESEARCH DALLAS
SMITH 20 RESEARCH DALLAS
SCOTT 20 RESEARCH DALLAS
WARD 30 SALES CHICAGO
TURNER 30 SALES CHICAGO
ALLEN 30 SALES CHICAGO
JAMES 30 SALES CHICAGO
BLAKE 30 SALES CHICAGO
MARTIN 30 SALES CHICAGO
NULL 40 OPERATIONS BOSTON
사원이 없는 부서(부서번호 40)도 포함되어 있다.
FULL OUTER JOIN은 좌측과 우측 테이블의 모든 데이터를 결합하여 반환한다. 일치하지 않는 행은 NULL로 채워진다.
DEPT 테이블과 DEPT_TEMP 테이블을 FULL OUTER JOIN으로 결합하는 쿼리문은 아래와 같다.
UPDATE DEPT_TEMP SET DEPTNO = DEPTNO + 20;
SELECT * FROM DEPT_TEMP;
-- DEPT_TEMP 테이블
DEPTNO DNAME LOC
------ ------------ ---------
30 ACCOUNTING NEW YORK
40 R&D DALLAS
50 MARKETING CHICAGO
60 OPERATIONS BOSTON
SELECT *
FROM DEPT
FULL OUTER JOIN DEPT_TEMP ON DEPT.DEPTNO = DEPT_TEMP.DEPTNO;
OUTER키워드를 생략하면 아래와 같다.
SELECT *
FROM DEPT
FULL JOIN DEPT_TEMP ON DEPT.DEPTNO = DEPT_TEMP.DEPTNO;
또는, LEFT JOIN과 RIGHT JOIN을 사용하여 구현하면 아래와 같다.
SELECT L.DEPTNO, L.DNAME, L.LOC, R.DEPTNO, R.DNAME, R.LOC
FROM DEPT L
LEFT OUTER JOIN DEPT_TEMP R ON L.DEPTNO = R.DEPTNO
UNION
SELECT L.DEPTNO, L.DNAME, L.LOC, R.DEPTNO, R.DNAME, R.LOC
FROM DEPT L
RIGHT OUTER JOIN DEPT_TEMP R ON L.DEPTNO = R.DEPTNO;
-- 실행 결과
DEPTNO L.DNAME L.LOC R.DEPTNO R.DNAME R.LOC
------ ------------ --------- -------- ------------ ---------
10 ACCOUNTING NEW YORK NULL NULL NULL
20 RESEARCH DALLAS NULL NULL NULL
30 SALES CHICAGO 30 ACCOUNTING NEW YORK
NULL NULL NULL 40 R&D DALLAS
NULL NULL NULL 50 MARKETING CHICAGO
NULL NULL NULL 60 OPERATIONS BOSTON
DEPT 테이블과 DEPT_TEMP 테이블 모두에 존재하지 않는 부서 정보도 포함되어 있다.
- LEFT OUTER JOIN: 기준 테이블의 모든 데이터를 반환하며, 일치하지 않는 데이터는 NULL로 채워짐.
- RIGHT OUTER JOIN: JOIN되는 테이블의 모든 데이터를 반환하며, 일치하지 않는 데이터는 NULL로 채워짐.
- FULL OUTER JOIN: 두 테이블의 모든 데이터를 결합하여 반환하며, 일치하지 않는 데이터는 NULL로 채워짐.
OUTER JOIN은 데이터 간의 관계를 명확하게 이해하고, 일치하지 않는 데이터를 포함하여 결과를 반환할 때 유용하다. 각각의 JOIN 유형을 적절히 사용하여 필요한 데이터를 효율적으로 추출할 수 있다.

INNER JOIN은 두 테이블에서 일치하는 키 값을 가진 행만 반환한다. 예를 들어, TAB1과 TAB2가 있다고 가정하자.
-- TAB1
키 값 | 데이터
-----------
B | Data1_B
C | Data1_C
D | Data1_D
E | Data1_E
-- TAB1
키 값 | 데이터
-----------
A | Data2_A
B | Data2_B
C | Data2_C
아래의 쿼리문을 실행시키면 결과는 아래와 같다.
SELECT TAB1.키 값, TAB1.데이터, TAB2.데이터
FROM TAB1
INNER JOIN TAB2 ON TAB1.키 값 = TAB2.키 값;
-- 실행 결과
키 값 | 데이터(TAB1) | 데이터(TAB2)
-------------------------------
B | Data1_B | Data2_B
C | Data1_C | Data2_C
LEFT OUTER JOIN은 좌측 테이블(TAB1)을 기준으로, 우측 테이블(TAB2)에서 일치하지 않는 경우 NULL을 반환한다.
SELECT TAB1.키 값, TAB1.데이터, TAB2.데이터
FROM TAB1
LEFT OUTER JOIN TAB2 ON TAB1.키 값 = TAB2.키 값;
-- 실행 결과
키 값 | 데이터(TAB1) | 데이터(TAB2)
-------------------------------
B | Data1_B | Data2_B
C | Data1_C | Data2_C
D | Data1_D | NULL
E | Data1_E | NULL
RIGHT OUTER JOIN은 우측 테이블(TAB2)을 기준으로, 좌측 테이블(TAB1)에서 일치하지 않는 경우 NULL을 반환한다.
SELECT TAB1.키 값, TAB1.데이터, TAB2.데이터
FROM TAB1
RIGHT OUTER JOIN TAB2 ON TAB1.키 값 = TAB2.키 값;
키 값 | 데이터(TAB1) | 데이터(TAB2)
-------------------------------
A | NULL | Data2_A
B | Data1_B | Data2_B
C | Data1_C | Data2_C
FULL OUTER JOIN은 양쪽 테이블의 모든 데이터를 결합하여 반환한다. 일치하지 않는 행은 NULL로 채워진다.
SELECT TAB1.키 값, TAB1.데이터, TAB2.데이터
FROM TAB1
FULL OUTER JOIN TAB2 ON TAB1.키 값 = TAB2.키 값;
키 값 | 데이터(TAB1) | 데이터(TAB2)
-------------------------------
A | NULL | Data2_A
B | Data1_B | Data2_B
C | Data1_C | Data2_C
D | Data1_D | NULL
E | Data1_E | NULL
CROSS JOIN은 두 테이블의 가능한 모든 행의 조합을 반환한다.
SELECT TAB1.키 값, TAB1.데이터, TAB2.키 값, TAB2.데이터
FROM TAB1
CROSS JOIN TAB2;
-- 실행 결과
TAB1.키 값 | TAB1.데이터 | TAB2.키 값 | TAB2.데이터
----------------------------------------------
B | Data1_B | A | Data2_A
B | Data1_B | B | Data2_B
B | Data1_B | C | Data2_C
C | Data1_C | A | Data2_A
C | Data1_C | B | Data2_B
C | Data1_C | C | Data2_C
D | Data1_D | A | Data2_A
D | Data1_D | B | Data2_B
D | Data1_D | C | Data2_C
E | Data1_E | A | Data2_A
E | Data1_E | B | Data2_B
E | Data1_E | C | Data2_C