JOIN은 두 개 이상의 테이블을 연결하거나 결합하여 데이터를 조회하는 SQL 연산이다. 이는 관계형 데이터베이스의 핵심 기능 중 하나로, 다양한 조건을 만족하는 복잡한 쿼리를 작성할 때 필수적이다. JOIN은 보통 PRIMARY KEY(PK)와 FOREIGN KEY(FK)의 관계에 의해 이루어지지만, PK와 FK 관계가 없어도 논리적인 값들의 연관만으로 JOIN이 성립될 수 있다.
선수 테이블과 팀 테이블이 있을 때, 선수 테이블의 데이터를 검색하고 관련된 팀 테이블의 데이터를 JOIN하여 검색할 수 있다. 이 과정은 선수, 팀, 운동장 등 여러 테이블을 조인하여 데이터를 조합할 수 있다.
A, B, C, D를 JOIN할 경우, 옵티마이저는 ( ( (A JOIN D) JOIN C) JOIN B)와 같이 순차적으로 JOIN을 처리한다.EQUI JOIN은 두 테이블 간의 칼럼 값들이 정확히 일치하는 경우에 사용하는 JOIN 방법이다. 대부분 PK와 FK 관계를 기반으로 하지만, 반드시 이 관계만을 사용하는 것은 아니다.
EQUI JOIN의 특징은 다음과 같다.
“=” 연산자를 사용하여 JOIN 조건을 표현한다.-- WHERE 절을 사용한 EQUI JOIN
SELECT PLAYER.PLAYER_NAME 선수명, TEAM.TEAM_NAME 소속팀명
FROM PLAYER, TEAM
WHERE PLAYER.TEAM_ID = TEAM.TEAM_ID;
-- INNER JOIN을 명시하여 사용하는 EQUI JOIN
SELECT PLAYER.PLAYER_NAME 선수명, TEAM.TEAM_NAME 소속팀명
FROM PLAYER INNER JOIN TEAM
ON PLAYER.TEAM_ID = TEAM.TEAM_ID;
INNER JOIN의 조건
N-1개 이상이다.JOIN은 관계형 데이터베이스의 필수적인 기능으로, 다양한 테이블 간의 데이터를 결합하여 원하는 정보를 조회할 수 있게 한다. EQUI JOIN은 이러한 JOIN 중에서도 가장 기본적인 형태로, 두 테이블 간의 칼럼 값이 정확히 일치하는 조건을 사용한다. 이를 통해 복잡한 쿼리를 쉽게 작성하고, 다양한 데이터를 조합하여 출력할 수 있다.
EQUI JOIN을 이용하여 선수(PLAYER) 테이블과 팀(TEAM) 테이블에서 K-리그 소속 선수들의 이름, 백넘버, 소속된 팀명, 연고지를 조회하는 사례를 설명한다.

-- 기본 EQUI JOIN
SELECT
PLAYER.PLAYER_NAME 선수명,
PLAYER.BACK_NO 백넘버,
PLAYER.TEAM_ID 팀코드,
TEAM.TEAM_NAME 팀명,
TEAM.REGION_NAME 연고지
FROM
PLAYER, TEAM
WHERE
PLAYER.TEAM_ID = TEAM.TEAM_ID;
-- INNER JOIN을 명시한 EQUI JOIN
SELECT
PLAYER.PLAYER_NAME 선수명,
PLAYER.BACK_NO 백넘버,
PLAYER.TEAM_ID 팀코드,
TEAM.TEAM_NAME 팀명,
TEAM.REGION_NAME 연고지
FROM
PLAYER INNER JOIN TEAM
ON PLAYER.TEAM_ID = TEAM.TEAM_ID;
| 선수명 | 백넘버 | 팀코드 | 팀명 | 연고지 |
|---|---|---|---|---|
| 이고르 | 21 | K06 | 아이파크 | 부산 |
| 오비나 | 26 | K10 | 시티즌 | 대전 |
| 윤원일 | 45 | K02 | 삼성블루윙즈 | 수원 |
| 페르난도 | 44 | K04 | 유나이티드 | 인천 |
| 레오 | 45 | K03 | 스틸러스 | 포항 |
| 실바 | 45 | K07 | 드래곤즈 | 전남 |
| 무스타파 | 77 | K04 | 유나이티드 | 인천 |
| 에디 | 7 | K01 울산현대 | 울산 | |
| 알리송 | 14 | K01 | 울산현대 | 울산 |
| 쟈스민 | 33 | K08 | 일화천마 | 성남 |
| 디디 | 8 | K06 | 아이파크 | 부산 |
테이블명이 길고 SQL의 복잡도가 높아지면 가독성을 높이기 위해 ALIAS를 사용할 수 있다.
-- ALIAS를 사용한 EQUI JOIN
SELECT
P.PLAYER_NAME 선수명,
P.BACK_NO 백넘버,
P.TEAM_ID 팀코드,
T.TEAM_NAME 팀명,
T.REGION_NAME 연고지
FROM
PLAYER P, TEAM T
WHERE
P.TEAM_ID = T.TEAM_ID;
-- INNER JOIN을 명시한 ALIAS 사용
SELECT
P.PLAYER_NAME 선수명,
P.BACK_NO 백넘버,
P.TEAM_ID 팀코드,
T.TEAM_NAME 팀명,
T.REGION_NAME 연고지
FROM
PLAYER P INNER JOIN TEAM T
ON P.TEAM_ID = T.TEAM_ID;
ALIAS를 사용해도 결과는 위와 동일하다.
EQUI JOIN 조건 외에 WHERE 절을 사용하여 추가적인 검색 조건을 적용하는 방법에 대해 설명한다. 예를 들어, 특정 포지션의 선수들만 조회하거나, 특정 조건에 따라 데이터를 필터링할 수 있다.
-- 기본 EQUI JOIN SQL
SELECT P.PLAYER_NAME 선수명, P.BACK_NO 백넘버, T.REGION_NAME 연고지, T.TEAM_NAME 팀명
FROM PLAYER P, TEAM T
WHERE P.TEAM_ID = T.TEAM_ID;
위의 기본 EQUI JOIN에 추가 조건을 WHERE 절에 넣어 특정 조건의 데이터를 필터링할 수 있다. 예를 들어, 포지션이 골키퍼('GK')인 선수들만 조회하고, 백넘버 순으로 정렬하는 경우를 살펴보자.
-- WHERE 절에 추가 조건을 포함한 SQL
SELECT P.PLAYER_NAME 선수명, P.BACK_NO 백넘버, T.REGION_NAME 연고지, T.TEAM_NAME 팀명
FROM PLAYER P, TEAM T
WHERE P.TEAM_ID = T.TEAM_ID AND P.POSITION = 'GK'
ORDER BY P.BACK_NO;
-- INNER JOIN을 사용한 SQL
SELECT P.PLAYER_NAME 선수명, P.BACK_NO 백넘버, T.REGION_NAME 연고지, T.TEAM_NAME 팀명
FROM PLAYER P INNER JOIN TEAM T
ON P.TEAM_ID = T.TEAM_ID
WHERE P.POSITION = 'GK'
ORDER BY P.BACK_NO;
실행 결과는 다음과 같다.
| 선수명 | 백넘버 | 연고지 | 팀명 |
|---|---|---|---|
| 최종문 | 1 | 전남 | 드래곤즈 |
| 정병지 | 1 | 포항 | 스틸러스 |
| 박유석 | 1 | 부산 | 아이파크 |
| 김승준 | 1 | 대전 | 시티즌 |
| 이현 | 1 | 인천 | 유나이티드 |
| 김운재 | 1 | 수원 | 삼성블루윙즈 |
| 정해운 | 1 | 성남 | 일화천마 |
| 권정혁 | 1 | 울산 | 울산현대 |
| 최동석 | 1 | 서울 | FC서울 |
| 김창민 | 1 | 전북 | 현대모터스 |
| ... | ... | ... | ... |
SQL 문장에서 테이블에 대한 ALIAS를 정의했을 경우, SELECT 절과 WHERE 절에서 테이블명이 아닌 ALIAS를 사용해야 한다. 그렇지 않으면 DBMS가 에러를 발생시킬 수 있다.
-- 잘못된 예제(오류 발생)
SELECT PLAYER.PLAYER_NAME 선수명, P.BACK_NO 백넘버, T.REGION_NAME 연고지, T.TEAM_NAME 팀명
FROM PLAYER P, TEAM T
WHERE P.TEAM_ID = TEAM.TEAM_ID AND P.POSITION = 'GK'
ORDER BY P.BACK_NO;
이 경우, TEAM.TEAM_ID 대신 T.TEAM_ID를 사용해야 한다.
팀(TEAM) 테이블과 구장(STADIUM) 테이블을 EQUI JOIN하여 각 팀의 전용 구장 정보를 출력한다.
-- 기본 EQUI JOIN SQL
SELECT TEAM.REGION_NAME, TEAM.TEAM_NAME, TEAM.STADIUM_ID, STADIUM.STADIUM_NAME, STADIUM.SEAT_COUNT
FROM TEAM, STADIUM
WHERE TEAM.STADIUM_ID = STADIUM.STADIUM_ID;
-- INNER JOIN을 사용한 SQL
SELECT TEAM.REGION_NAME, TEAM.TEAM_NAME, TEAM.STADIUM_ID, STADIUM.STADIUM_NAME, STADIUM.SEAT_COUNT
FROM TEAM INNER JOIN STADIUM
ON TEAM.STADIUM_ID = STADIUM.STADIUM_ID;
-- ALIAS를 사용한 SQL
SELECT T.REGION_NAME, T.TEAM_NAME, T.STADIUM_ID, S.STADIUM_NAME, S.SEAT_COUNT
FROM TEAM T, STADIUM S
WHERE T.STADIUM_ID = S.STADIUM_ID;
실행 결과는 다음과 같다.
| REGION_NAME | TEAM_NAME | STADIUM_ID | STADIUM_NAME | SEAT_COUNT |
|---|---|---|---|---|
| 전북 | 현대모터스 | D03 | 전주월드컵경기장 | 28000 |
| 성남 | 일화천마 | B02 | 성남종합운동장 | 27000 |
| 포항 | 스틸러스 | C06 | 포항스틸야드 | 25000 |
| 전남 | 드래곤즈 | D01 | 광양전용경기장 | 20009 |
| 서울 | FC서울 | B05 | 서울월드컵경기장 | 66806 |
| 인천 | 유나이티드 | B01 | 인천월드컵경기장 | 35000 |
| ... | ... | ... | ... | ... |
Non EQUI(비등가) JOIN은 두 개의 테이블 간의 칼럼 값들이 정확하게 일치하지 않는 경우에 사용되는 JOIN 방식이다. EQUI JOIN이 = 연산자를 사용하는 반면, Non EQUI JOIN은 BETWEEN, >, >=, <, <= 등의 연산자를 사용하여 JOIN을 수행한다.
BETWEEN, >, >=, <, <= 등의 연산자를 사용한다.사원(EMP) 테이블과 급여등급(SAL_GRADE) 테이블을 사용하여 사원들의 급여가 어느 등급에 속하는지를 조회하는 예시이다.
EMP 테이블
| ENAME | JOB | SAL |
|---|---|---|
| SMITH | Clerk | 800 |
| JAMES | Clerk | 950 |
| ADAMS | Clerk | 1100 |
| WARD | Sales | 1250 |
| MARTIN | Sales | 1250 |
| MILLER | Clerk | 1300 |
| TURNER | Sales | 1500 |
| ALLEN | Sales | 1600 |
| CLARK | Manager | 2450 |
| BLAKE | Manager | 2850 |
| JONES | Manager | 2975 |
| SCOTT | Analyst | 3000 |
| FORD | Analyst | 3000 |
| KING | President | 5000 |
SALGRADE 테이블
| GRADE | LOSAL | HISAL |
|---|---|---|
| 1 | 700 | 1200 |
| 2 | 1201 | 1400 |
| 3 | 1401 | 2000 |
| 4 | 2001 | 3000 |
| 5 | 3001 | 9999 |
-- Non EQUI JOIN SQL
SELECT E.ENAME 사원명, E.SAL 급여, S.GRADE 급여등급
FROM EMP E, SALGRADE S
WHERE E.SAL BETWEEN S.LOSAL AND S.HISAL;
실행 결과
| 사원명 | 급여 | 급여등급 |
|---|---|---|
| SMITH | 800 | 1 |
| JAMES | 950 | 1 |
| ADAMS | 1100 | 1 |
| WARD | 1250 | 2 |
| MARTIN | 1250 | 2 |
| MILLER | 1300 | 2 |
| TURNER | 1500 | 3 |
| ALLEN | 1600 | 3 |
| CLARK | 2450 | 4 |
| BLAKE | 2850 | 4 |
| JONES | 2975 | 4 |
| SCOTT | 3000 | 4 |
| FORD | 3000 | 4 |
| KING | 5000 | 5 |

BETWEEN 외에도 “>”나 “<”와 같은 연산자를 사용하면 Non EQUI JOIN에 해당한다.
세 개 이상의 테이블을 JOIN하는 경우를 설명한다. 예를 들어, 선수(PLAYER), 팀(TEAM), 운동장(STADIUM) 테이블을 사용하여 선수별 홈그라운드 경기장을 조회할 수 있다.
-- 기본 JOIN
SELECT P.PLAYER_NAME 선수명, P.POSITION 포지션, T.REGION_NAME 연고지, T.TEAM_NAME 팀명, S.STADIUM_NAME 구장명
FROM PLAYER P, TEAM T, STADIUM S
WHERE P.TEAM_ID = T.TEAM_ID
AND T.STADIUM_ID = S.STADIUM_ID
ORDER BY 선수명;
-- INNER JOIN 사용
SELECT P.PLAYER_NAME 선수명, P.POSITION 포지션, T.REGION_NAME 연고지, T.TEAM_NAME 팀명, S.STADIUM_NAME 구장명
FROM PLAYER P
INNER JOIN TEAM T ON P.TEAM_ID = T.TEAM_ID
INNER JOIN STADIUM S ON T.STADIUM_ID = S.STADIUM_ID
ORDER BY 선수명;
실행 결과는 다음과 같다.
| 선수명 | 포지션 | 연고지 | 팀명 | 구장명 |
|---|---|---|---|---|
| 가비 | MF | 수원 | 삼성블루윙즈 | 수원월드컵경기장 |
| 가이모토 | DF | 성남 | 일화천마 | 성남종합운동장 |
| 강대희 | MF | 수원 | 삼성블루윙즈 | 수원월드컵경기장 |
| 강성일 | GK | 대전 | 시티즌 | 대전월드컵경기장 |
| 강용 | DF | 포항 | 스틸러스 | 포항스틸야드 |
| 강정훈 | MF | 대전 | 시티즌 | 대전월드컵경기장 |
| ... | ... | ... | ... | ... |
JOIN을 사용하여 여러 테이블 간의 논리적 연관성을 통해 다양한 데이터를 조합하고 조회할 수 있다. Non EQUI JOIN과 여러 테이블 JOIN은 이를 확장하여 복잡한 쿼리를 작성할 수 있게 해준다.