Database/Oracle

[SQL]DML(SELECT문)(8) - JOIN

Ma_Sand 2022. 3. 22. 22:13
반응형

JOIN

   : 둘 이상의 테이블에서 데이터를 같이 조회할 때 사용한다.

  - SELECT문을 이용한다.

  - 조회 결과는 하나의 결과물로 반환된다.

  - JOIN을 사용하는 이유

   : 관계형 데이터베이스에서 최소한의 데이터로 각각의 테이블에 데이터를 보관하기 위해서이다.

     이때, 테이블 간의 연결고리(컬럼)를 매칭시켜서 조회하여야 한다.

 

◎ 문법상 분류

  - 오라클 전용 구문 / ANSI(미국 국립 표준 협회) 구문

 

◎ 개념상 분류

오라클 전용 구문 ANSI 구문
등가조인(EQUAL JOIN) 내부조인(INNER JOIN) → JOIN USING/ON
포괄조인
(LEFT OUTER JOIN)
(RIGHT OUTER JOIN)
외부조인(OUTER JOIN) → JOIN USING
왼쪽 외부조인(LEFT OUTER JOIN)
오른쪽 외부조인(RIGHT OUTER JOIN)
전체 외부조인(FULL OUTER JOIN)
카테시안 곱(CARTESIAN PRODUCT) 교차조인(CROSS JOIN)
자체조인(SELF JOIN)
비등가조인(NON EQUAL JOIN)
다중조인(테이블 3개 이상 조인)

 

 

 (1) 등가 조인(EQUAL JOIN) / 내부 조인(INNER JOIN)

   : 연결할 컬럼값이 일치하는 행들만 조인되어 조회한다.

      → 일치하지 않는 행들은 조회되지 않는다.

  - 동등비교 연산자('=')를 사용한다.

 

  - 등가 조인(오라클 구문)

     SELECT 조회할 컬럼명1, 컬럼명2, ...

     FROM 조회할 테이블명1, 테이블명2, ...

     WHERE 연결할 컬럼에 대한 조건 제시('=')

 

  - 내부 조인(ANSI 구문) : ON 구문

     SELECT 조회할 컬럼명1, 컬럼명2, ...

     FROM 기준으로 삼을 테이블명 1개

     JOIN 조인할 테이블명 1개 ON (연결할 컬럼에 대한 조건('='))

 

  - 내부 조인(ANSI 구문) ; USING 구문

     SELECT 조회할 컬럼명1, 컬럼명2, ...

     FROM 기준으로 삼을 테이블명 1개

     JOIN 조인할 테이블명 1개 USING (연결할 컬럼명 1개)

 

  ** 연결할 컬럼명이 동일할 경우

       : USING 구문을 제외하고 나머지 구문들은 테이블명이나 별칭으로 어느 테이블의 컬럼명인지 적어야 한다.

         EX) THIS.NAME, ...

 

 

 - 오라클 구문

  1) 연결할 컬럼명이 서로 다른 경우

    - 전체 사원들의 사번, 사원명, 부서코드, 부서명을 조회하기

SELECT EMP_ID, EMP_NAME, DEPT_CODE, DEPT_TITLE
FROM EMPLOYEE, DEPARTMENT
WHERE DEPT_CODE = DEPT_ID;

 'WHERE DEPT_CODE = DEPT_ID'에서 DEPT_CODE와 DEPT_ID는 컬럼명이 서로 다르다. 이 두 컬럼들을 동등비교 연산자로 연결하여 컬럼값이 일치하는 행만 조인되어 조회한다. 여기서 일치하지 않는 값은 조회되지 않는다.

 

 

  2) 연결할 컬럼명이 서로 같은 경우

    - 전체 사원들의 사번, 사원명, 직급코드, 직급명을 조회하기

SELECT EMP_ID, EMP_NAME, JOB_CODE, JOB_NAME
FROM EMPLOYEE, JOB
WHERE JOB_CODE = JOB_CODE;

 'WHERE JOB_CODE = JOB_CODE'를 보면 각각의 JOB_CODE가 EMPLOYEE 테이블의 것인지 JOB 테이블의 것인지 명시되어 있지 않다. 이때 실행하면 'column ambiguously defined'라고 에러가 뜬다. 컬럼이 애매하게 정의되어 있다는 뜻이다. 정상적으로 실행시키려면 각각 어떤 테이블의 컬럼인지 아래와 같이 명시해주어야 한다.

 

-- 1) 테이블명을 붙여서 명시하기
SELECT EMP_ID, EMP_NAME, EMPLOYEE.JOB_CODE, JOB_NAME
FROM EMPLOYEE, JOB
WHERE EMPLOYEE.JOB_CODE = JOB.JOB_CODE;
-- 테이블명.컬럼명

-- 2) 별칭을 붙여서 명시하기
SELECT EMP_ID, EMP_NAME, E.JOB_CODE, JOB_NAME
FROM EMPLOYEE E, JOB J
WHERE E.JOB_CODE = J.JOB_CODE;
-- 별칭.컬럼명

 

 

 - ANSI 구문

  1) 연결할 컬럼명이 서로 다른 경우 (→ ON 구문을 사용한다.)

    - 전체 사원들의 사번, 사원명, 부서코드, 부서명을 조회하기

SELECT EMP_ID, EMP_NAME, DEPT_CODE, DEPT_TITLE
FROM EMPLOYEE
/*INNER*/ JOIN DEPARTMENT ON (DEPT_CODE = DEPT_ID);

 기준 테이블을 EMPLOYEE 테이블로 놓고 여기에 조인할 테이블을 DEPARTMENT로 놓는다. 그 다음 ON 구문으로 EMPLOYEE 테이블의 DEPT_CODE 컬럼과 DEPARTMENT 테이블의 DEPT_ID 컬럼을 조인한다. 이때 ON 구문을 사용하는 이유는 연결할 컬럼명이 서로 다르기 때문이다. 

 

 

  2) 연결할 컬럼명이 서로 같은 경우

    - 전체 사원들의 사번, 사원명, 직급코드, 직급명을 조회하기

    - ON 구문

-- 테이블명으로 구별
SELECT EMP_ID, EMP_NAME, EMPLOYEE.JOB_CODE, JOB_NAME
FROM EMPLOYEE
JOIN JOB ON (EMPLOYEE.JOB_CODE = JOB.JOB_CODE);

-- 별칭으로 구별
SELECT EMP_ID, EMP_NAME, E.JOB_CODE, JOB_NAME
FROM EMPLOYEE E
JOIN JOB J ON (E.JOB_CODE = J.JOB_CODE);

 ON 구문은 연결할 컬럼명이 서로 다를 때도 쓰지만, 연결할 컬럼명이 서로 같을 때도 쓴다. 이땐 테이블명이나 별칭으로 컬럼을 구별해주어야 한다. 그래야 다른 컬럼이라고 인식한다.

 테이블명으로 구별할 땐 ON 구문에서 연결할 컬럼들이 어떤 테이블의 컬럼인지 각각 해당 테이블명을 붙여준다. 그 다음 SELECT절에 해당 컬럼이 있는지 확인한 후, 있으면 어떤 테이블의 컬럼값을 출력할 것인지 해당 테이블명을 붙여준다.

 별칭으로 구별할 땐 먼저 각 테이블에 별칭을 붙여준다. 그 다음에 ON 구문에서 연결할 컬럼들을 구별할 수 있게 각 테이블에 붙여준 별칭을 각각 작성한다. 그리고 SELECT절에서 ON 구문에 있는 컬럼이 있는지 확인한 후, 해당 컬럼이 있으면 어떤 테이블의 컬럼을 출력할 것인지 해당 별칭을 붙여주면 된다.

 

    - USING 구문

SELECT EMP_ID, EMP_NAME, JOB_CODE, JOB_NAME
FROM EMPLOYEE 
JOIN JOB USING(JOB_CODE);

USING 구문은 컬럼명이 같을 때 쓰는 구문이므로 하나의 컬럼명을 어떤 테이블의 컬럼인지 명시해주지 않아도 알아서 매칭시킨다.

 

 cf) 자연 조인(NATURAL JOIN)

    : 등가조인의 하나로, 동일한 타입과 이름을 가진 컬럼을 조인 조건으로 이용한다.

SELECT EMP_ID, EMP_NAME, JOB_CODE, JOB_NAME
FROM EMPLOYEE
NATURAL JOIN JOB;

두 개의 테이블에 일치하는 컬럼이 단 하나만 존재할 경우에 알아서 매칭시킨다.

 

 

 

 

 (2) 포괄 조인(LEFT/RIGHT OUTER JOIN) / 외부조인(OUTER JOIN)

   : 연결한 컬럼값이 일치하지 않는 행도 포함시켜 조회한다.

     → 일치하는 행에서 기준이 되는 테이블을 기준으로 일치하지 않는 행도 포함시켜 조회한다.

  - 기준이 되는 테이블을 지정하기 위해 반드시 LEFT/RIGHT를 지정해야 한다.

 

   LEFT OUTER JOIN

    : 두 테이블 중 왼편에 기술한 테이블을 기준으로 조인한다.

      → 데이터값을 왼편에 기술한 테이블의 데이터는 일치하지 않아도 무조건 조회된다.

 

   - 전체 사원들의 사원명, 급여, 부서명을 조회하기

   - 오라클 구문

SELECT EMP_NAME, SALARY, DEPT_TITLE
FROM EMPLOYEE, DEPARTMENT
WHERE DEPT_CODE = DEPT_ID(+);

 'WHERE DEPT_CODE = DEPT_ID'에서 기준으로 삼을 테이블의 컬럼명이 아닌 반대편 테이블의 컬럼명에 '(+)'을 붙여줘야 한다. 기준이 되는 테이블은 왼편에 있는 EMPLOYEE이므로 EMPLOYEE 테이블의 컬럼인 DEPT_CODE가 아닌 DEPARTMENT(반대편 테이블) 테이블의 컬럼인 DEPT_ID에 '(+)'을 붙여주면 된다.

 그러면 이와 같이 출력된다. EMPLOYEE 테이블에 사원들의 기본 정보(이름, 사번, 주민등록번호 등)가 포함되어 있으므로 부서가 배정되지 않아 부서코드가 없는 사원들은 부서명이 NULL값으로 나온다.

 

 

 

   - ANSI 구문

SELECT EMP_NAME, SALARY, DEPT_TITLE
FROM EMPLOYEE
LEFT /*OUTER*/ JOIN DEPARTMENT ON (DEPT_CODE = DEPT_ID);

 기준이 되는 테이블은 왼편에 있는 EMPLOYEE이므로 EMPLOYEE 테이블에 존재하는 데이터를 조회한다. 

 ANSI 구문에서는 LEFT OUTER JOIN을 사용하므로 오라클 구문과 달리 '(+)'를 사용하지 않는다. 이때 'OUTER'는 생략할 수 있다.

 

  RIGHT OUTER JOIN

    : 두 테이블 중 오른편에 기술된 테이블을 기준으로 조인한다.

      → 데이터값을 오른편에 기술한 테이블의 데이터는 일치하지 않아도 무조건 조회된다.

   - 전체 사원들의 사원명, 급여, 부서명을 조회하기

   - 오라클 구문

SELECT EMP_NAME, SALARY, DEPT_TITLE
FROM EMPLOYEE, DEPARTMENT
WHERE DEPT_CODE(+) = DEPT_ID;

 오른편에 있는 DEPARTMENT를 기준으로 조인한다. DEPARTMENT 테이블의 DEPT_ID 컬럼이 아닌 EMPLOYEE 테이블의 DEPT_CODE에 '(+)'을 붙이면 된다. 그러면 이와 같이 출력된다.

 DEPARTMENT 테이블에는 부서코드와 부서명, 지역코드가 있고, 사원들의 기본 정보는 없으므로 EMP_NAME(사원이름)와 SALARY(연봉)은 NULL값이 나온다.

 

 

 

   - ANSI 구문

SELECT EMP_NAME, SALARY, DEPT_TITLE
FROM EMPLOYEE
RIGHT /*OUTER*/ JOIN DEPARTMENT ON (DEPT_CODE = DEPT_ID);

 오른편에 있는 DEPARTMENT 테이블을 기준으로 조인한다. 그러면 EMPLOYEE 테이블에는 없는 데이터도 출력되므로 그 데이터들은 NULL값으로 출력된다.

 

   FULL OUTER JOIN

   : 두 테이블이 가진 모든 행들을 조회한다.

  - 일치하는 행들과 LEFT OUTER JOIN 기준으로 추가된 행들, RIGHT OUTER JOIN 기준으로 추가된 행들이 조회된다.

SELECT EMP_NAME, SALARY, DEPT_TITLE
FROM EMPLOYEE
FULL /*OUTER*/ JOIN DEPARTMENT ON (DEPT_CODE = DEPT_ID);

 위에서 LEFT OUTER JOIN과 RIGHT OUTER JOIN을 했을 때 나온 것들이 합쳐져서 출력되었다. EMPLOYEE 테이블과 DEPARTMENT 테이블을 모두 기준 삼아 조회되기 때문이다. 서로 없는 행들도 각자 기준에서 모두 NULL로 출력된다.

 

 위 구문은 ANSI 구문인데, 오라클 구문을 적지 않은 이유는

이 FULL OUTER JOIN은 오라클 구문에선 실행되지 않으므로 적지 않았다.

 

 

 

 

 

 

 

 

 (3) 카테시안 곱(CATESIAN PRODUCT) / 교차조인(CROSS JOIN)

   : 모든 테이블의 각 행들이 서로 매칭된 데이터가 조회된다.

     즉, 두 테이블의 행들이 모두 곱해진 행들의 조합이 출력된다.

  - 각각 n개, m개의 행을 가진 테이블들의 카테시안 곱의 결과는 n*m행이다.

  - 모든 경우의 수를 전부 조회하므로 방대한 데이터를 출력해 과부화의 위험이 있다.

 

 - 사원들의 이름과 해당 부서명을 조회하기

   - 오라클 구문

SELECT EMP_NAME, DEPT_TITLE
FROM EMPLOYEE, DEPARTMENT;

 ⇒ (EMPLOYEE 테이블) 23행 * (DEPARTMENT 테이블) 9행으로 총 207행이 조회되었다.

 

   - ANSI 구문

SELECT EMP_NAME, DEPT_CODE
FROM EMPLOYEE
CROSS JOIN DEPARTMENT;

WHERE절에 기술하는 조인 조건이 잘못됐거나 아예 없을 경우에 발생하는 구문이다.

 

 

 

 

 (4) 비등가 조인(NON EQUAL JOIN)

   : 지정한 컬럼값들이 일치하는 경우가 아니라 범위에 포함되는 경우를 매칭해서 조회한다.

  - 등호 '='를 사용하지 않고 다른 비교 연산자로 조인한다.(>, <, >=, <=, BETWEEN A AND B)

   cf) 등가조인: '='로 일치하는 경우에만 조회한다.

 

 - 사원들의 이름과 급여, 급여등급을 조회하기

   - 오라클 구문

SELECT EMP_NAME, SALARY, S.SAL_LEVEL
FROM EMPLOYEE, SAL_GRADE S
WHERE SALARY BETWEEN MIN_SAL AND MAX_SAL;

'BETWEEN A AND B'는 A 이상 B 이하의 범위이다. 위의 WHERE절은 SALARY(급여)가 MIN_SAL(최저 급여 등급) 이상 MAX_SAL(최고 급여 등급) 이하의 범위일 때 조회한다.

 

   - ANSI 구문

SELECT EMP_NAME, SALARY, S.SAL_LEVEL
FROM EMPLOYEE
JOIN SAL_GRADE S ON (SALARY >= MIN_SAL AND SALARY <= MAX_SAL);

'='를 제외한 비교 연산자로 급여가 최저 급여 등급보다 크거나 같을 때와 최고 등급보다 작거나 같을 때 조회한다.

 

 

 

 

 (5) 자체 조인(SELF JOIN)

   : 같은 테이블끼리 조인하는 경우에 조회한다. 즉, 자기 자신과 조인하여 조회한다.

  - 자체 조인은 반드시 서로 다른 테이블인 것처럼 각 테이블에 별칭을 붙여줘야 한다.

 

 - 사원들의 사번과 이름, 사수의 사번과 이름을 조회하기

   - 오라클 구문

SELECT E.EMP_ID "사번", E.EMP_NAME "사원 이름", E.MANAGER_ID "사수 사번", M.EMP_NAME "사수 이름"
FROM EMPLOYEE E, EMPLOYEE M
WHERE E.MANAGER_ID = M.EMP_ID(+);

 사원에 대한 정보를 뽑아낼 EMPLOYEE 테이블에 'E'라는 별칭을 붙이고, 사수에 대한 정보를 뽑아낼 EMPLOYEE 테이블에 'M'이라는 별칭을 붙인다. 사원들에 대한 정보인 사번(EMP_ID)과 사원 이름(EMP_NAME), 사수의 사번(MANAGER_ID)에 별칭 'E'를, 사수에 대한 정보인 사수 이름(EMP_NAME)과 사번(EMP_ID)에 별칭 'M'을 붙인다.

 그리고 WHERE절에서 사원 정보를 뽑아내는 'E' 테이블을 기준으로 조인하기 위해 LEFT OUTER JOIN을 한다.

 

   - ANSI 구문

SELECT E.EMP_ID "사번", E.EMP_NAME "사원 이름", E.MANAGER_ID "사수 사번", M.EMP_NAME "사수 이름"
FROM EMPLOYEE E
LEFT JOIN EMPLOYEE M ON (E.MANAGER_ID = M.EMP_ID);

위 오라클 구문과 같다.

 

 

 

 

 (6) 다중 조인

   : 3개 이상의 테이블을 조인하여 조회한다. 이때 조인하는 순서가 중요하다.

 

 - 사원들의 사번과 이름, 부서 이름, 직급 이름을 조회하기

SELECT * FROM EMPLOYEE;
SELECT * FROM DEPARTMENT;
SELECT * FROM JOB;

 우선 각 테이블들을 따로 조회한 후 추출할 값이 있는 컬럼들을 확인한다.

 EMPLOYEE 테이블에선 EMP_ID(사번)와 EMP_NAME(사원 이름), DEPT_CODE(부서 코드), JOB_CODE(직급 코드) 컬럼을 사용할 것이고, DEPARTMENT 테이블에선 DEPT_ID(부서 번호)와 DEPT_TITLE(부서 이름) 컬럼을, JOB 테이블에선 JOB_CODE(직급 코드), JOB_NAME(직급 이름)을 사용할 것이다.

 

   - 오라클 구문

SELECT EMP_ID "사번", EMP_NAME "사원 이름", DEPT_TITLE "부서 이름", JOB_NAME "직급 이름"
FROM EMPLOYEE E, DEPARTMENT D, JOB J
WHERE E.DEPT_CODE = D.DEPT_ID(+) 
AND E.JOB_CODE = J.JOB_CODE(+);

 EMPLOYEE 테이블을 기준으로 LEFT OUTER JOIN을 할 것이므로 EMPLOYEE 테이블을 왼편에 작성한다. 그리고 WHERE절의 'DEPT_CODE = DEPT_ID'에서 EMPLOYEE 테이블의 컬럼인 DEPT_CODE의 반대편에 있는 DEPARTMENT 테이블의 컬럼인 DEPT_ID에 '(+)'를 붙여준다.

 그 다음, AND의 'JOB_CODE = JOB_CODE'에 EMPLOYEE 테이블과 JOB 테이블에 JOB_CODE라는 같은 이름의 컬럼이 있으므로 별칭을 붙여준다. 이때, 왼편에 EMPLOYEE 테이블의 JOB_CODE를 작성하고 오른쪽에 JOB 테이블의 JOB_CODE를 작성해야 한다. 그래야 EMPLOYEE 테이블을 기준으로 LEFT 조인이 된다. 그러면 'E.JOB_CODE = J.JOB_CODE'가 된다.

 

   - ANSI 구문

SELECT EMP_ID "사번", EMP_NAME "사원 이름", DEPT_TITLE "부서 이름", JOB_NAME "직급 이름"
FROM EMPLOYEE E
LEFT JOIN DEPARTMENT D ON (E.DEPT_CODE = D.DEPT_ID)
LEFT JOIN JOB J ON(E.JOB_CODE = J.JOB_CODE);

오라클 구문과 순서를 똑같이 작성하면 된다.

단, FROM절에 기준으로 할 테이블을 작성하고, 이 테이블에 조인할 테이블을 LEFT JOIN절에 적으면 된다. 그리고 해당 절에 해당 테이블에 대한 조건식을 ON 구문에 작성하면 된다.

반응형