본문 바로가기

SQLD

[SQLD : Ⅲ. SQL 기본] 9. 조인

* JOIN 개요

 두 개 이상의 테이블 들을 연결 또는 결합하여 데이터를 출력하는 것을 JOIN이라고 하며,

 일반적으로 사용되는 SQL 문장의 상당수가 JOIN

 일반적인 경우 행들은 PRIMARY KEY(PK)나 FOREIGN KEY(FK) 값의 연관에 의해 JOIN이 성립

 FROM 절에 여러 테이블이 나열되더라도 SQL에서 데이터를 처리할 때는 단 두 개의 집합 간에만 조인이 일어난다

 

* EQUI JOIN

  EQUI(등가) JOIN은 두 개의 테이블 간에 칼럼 값들이 서로 정확하게 일치하는 경우에 사용되는 방법

  대부분 PK ↔ FK의 관계를 기반으로 함

  반드시 PK ↔ FK의 관계로만 EQUI JOIN이 성립하는 것은 아님

  계층형(Hierarchical)이나 망형(Network) 데이터베이스와 비교해서 관계형 데이터베이스의 큰 장점

SELECT 테이블1.칼럼명, 테이블2.칼럼명, ... 
FROM 테이블1, 테이블2 
WHERE 테이블1.칼럼명1 = 테이블2.칼럼명2; → WHERE 절에 JOIN 조건을 넣는다.

 ANSI/ISO SQL 표준 방식

SELECT 테이블1.칼럼명, 테이블2.칼럼명, ... 
FROM 테이블1 INNER JOIN 테이블2 ON 테이블1.칼럼명1 = 테이블2.칼럼명2; → ON 절에 JOIN 조건을 넣는다

[예제] 선수 테이블과 팀 테이블에서 선수 이름과 소속된 팀의 이름을 출력하시오.

SELECT PLAYER.PLAYER_NAME 선수명, TEAM.TEAM_NAME 소속팀명 FROM PLAYER, 
       TEAM WHERE PLAYER.TEAM_ID = TEAM.TEAM_ID; 
       
또는 INNER JOIN을 명시하여 사용할 수도 있다.
SELECT PLAYER.PLAYER_NAME 선수명, TEAM.TEAM_NAME 소속팀명 
FROM PLAYER INNER JOIN TEAM ON PLAYER.TEAM_ID = TEAM.TEAM_ID;

JOIN에 사용되는 두 개의 테이블에 같은 칼럼명이 존재하는 경우에는

DBMS의 옵티마이저는 어떤 칼럼을 사용해야 할지 모르기 때문에 파싱 단계에서 에러가 발생

데이터만 출력하는 INNER JOIN에 참여하는 대상 테이블이 N개라고 했을 때,

N개의 테이블로부터 필요한 데이터를 조회하기 위해 대상 테이블은 N-1개 이상이 필요

 

* 선수-팀 EQUI JOIN 사례

선수(PLAYER) 테이블과 팀(TEAM) 테이블에서

 K-리그 소속 선수들의 이름, 백넘버와 그 선수가 소속되어 있는 팀명 및 연고지를 알고 싶다는 요구사항

데이터를 출력하기 위한 SELECT SQL 문장을 작성한다.

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을 명시하여 사용할 수도 있다. 
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;

[예제] 칼럼과 테이블에 ALIAS를 적용하여 위 SQL을 수정한다. 실행 결과는 ALIAS 적용 전과 같음을 확인 할 수 있다.

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을 명시하여 사용할 수도 있다. 
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;

 

* 선수-팀 WHERE 절 검색 조건 사례

 WHERE 절에서 JOIN 조건 이외의 검색 조건에 대한 제한 조건을 덧붙여 사용 가능

 EQUI JOIN의 최소한의 연관 관계를 위해서 테이블 개수 - 1개의 JOIN 조건을 WHERE 절에 명시하고,

 부수적인 제한 조건을 논리 연산자를 통하여 추가로 입력하는 것이 가능

 

[예제] 위 SQL 문장의 WHERE 절에 포지션이 골키퍼인(골키퍼에 대한 포지션 코드는 ‘GK’임) 선수들에 대한

데이터만을 백넘버 순으로 출력하는 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을 명시하여 사용할 수도 있다. 
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;

ALIAS를 적용해서 SQL 문장을 작성했을 경우, WHERE 절과 SELECT 절에는 테이블명이 아닌 ALIAS를 사용해야 함

권장 사항은 아니지만 하나의 SQL 문장 내에서 유일하게 사용하는 칼럼명이라면 칼럼명 앞에 ALIAS를 붙이지 않아도 됨

 

[예제] 위 SQL 문장에서 FROM 절에서 테이블에 대한 ALIAS를 정의했는데,

SELECT 절이나 WHERE 절에서 테이블명을 사용한다면 DBMS의 옵티마이저가 칼럼명이 부적합하다는 에러를 파싱 단계에서 발생시킨다. (SQL 문장의 파싱 순서는 FROM 절, WHERE 절, SELECT 절, ORDER BY 절 순서이다.)

 SELECT PLAYER.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; 
 
 SELECT PLAYER.PLAYER_NAME 선수명, P.BACK_NO 백넘버, * 1행에 오류: ERROR: 열명이 부적합하다.

 

* 팀-구장 EQUI JOIN 사례

[예제] 팀(TEAM) 테이블과 구장(STADIUM) 테이블의 관계를 이용해서 소속팀이 가지고 있는 전용구장의 정보를 팀의 정보와 함께 출력하는 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을 명시하여 사용할 수도 있다. 
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; 

 SQL문과 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; 

또는 INNER JOIN을 명시하여 사용할 수도 있다. 
SELECT T.REGION_NAME, T.TEAM_NAME, T.STADIUM_ID,
       S.STADIUM_NAME, S.SEAT_COUNT 
FROM TEAM T INNER JOIN STADIUM S ON T.STADIUM_ID = S.STADIUM_ID; 

중복이 되지 않는 칼럼의 경우 ALIAS를 사용하지 않아도 되므로, 
아래 SQL 문은 위 SQL문과 같은 결과를 얻을 수 있다. 
그러나 같은 이름을 가진 중복 칼럼의 경우는 테이블명이나 ALIAS가 필수 조건이다. 
SELECT REGION_NAME, TEAM_NAME, T.STADIUM_ID, 
       STADIUM_NAME, SEAT_COUNT 
FROM TEAM T, STADIUM S 
WHERE T.STADIUM_ID = S.STADIUM_ID;

 

* NON EQUI JOIN

  Non EQUI(비등가) JOIN은 두 개의 테이블 간에 칼럼 값들이 서로 정확하게 일치하지 않는 경우에 사용

  두 개의 테이블 간에 칼럼 값들이 서로 정확하게 일치하지 않는 경우에는 EQUI JOIN을 사용

   Non EQUI JOIN을 시도할 수 있으나 데이터 모델에 따라서 Non EQUI JOIN이 불가능한 경우도 있음

SELECT 테이블1.칼럼명, 테이블2.칼럼명, ... 
FROM 테이블1, 테이블2 
WHERE 테이블1.칼럼명1 BETWEEN 테이블2.칼럼명1 AND 테이블2.칼럼명2;

[예제] Non EQUI JOIN에 대한 샘플은 K-리그 관련 테이블로 구현되지 않으므로,

사원(EMP) 테이블과 가상의 급여등급(SAL_GRADE) 테이블로 설명을 하도록 한다.

어떤 사원이 받고 있는 급여가 어느 등급에 속하는 등급인지 알고 싶다는 요구사항에 대한 Non EQUI JOIN의 사례

SELECT E.ENAME, E.JOB, E.SAL, S.GRADE 
FROM EMP E, SALGRADE S
WHERE E.SAL BETWEEN S.LOSAL AND S.HISAL;

 사원들의 급여가 급여등급(SALGRADE) 테이블의 등급으로 표시되기 위해서는 “=” 연산자로 JOIN을 이용할 수가 없음

 데이터를 기준으로 급여등급 테이블의 어느 등급에 속하는지 1:1로 해당하는 값들을 나열

[예제] 사원 14명 모두에 대해 아래 SQL로 급여와 급여등급을 알아본다.

SELECT E.ENAME 사원명, E.SAL 급여, S.GRADE 급여등급 
FROM EMP E, SALGRADE S 
WHERE E.SAL BETWEEN S.LOSAL AND S.HISAL;

“=” 연산자가 아닌 “>”나 “<”와 같은 다른 연산자를 사용했을 경우에도 모두 Non EQUI JOIN에 해당

 

* 3개 이상 TABLE JOIN

[예제] 선수 테이블의 소속팀코드(TEAM_ID)가 팀 테이블의 팀코드(TEAM_ID)와 PK-FK의 관계가 있음,

운동장 테이블의 운동장코드(STADIUM_ID)와 팀 테이블의 전용구장코드(STADIUM_ID)가 PK-FK 관계

세 개의 테이블에 대한 JOIN이므로 WHERE 절에 2개 이상의 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 선수명;

데이터의 정합성에 더 큰 비용을 지불해야 하며, 데이터를 추가, 삭제, 수정하는 작업 역시 상당한 노력이 요구

오히려 검색 속도가 떨어질 수도 있음

테이블 간에 논리적인 연관관계가 필요하고 그런 관계성을 통해서 다양한 데이터들을 출력

JOIN을 잘못 기술하면 시스템 자원 부족, 과다한 응답시간 지연을 발생시키는 중요 원인이 됨