클라우드 융합 Full-stack 웹 개발자 양성과정/SQL, JDBC

데이터 베이스 구현 - SELECT(GROUP BY, JOIN)

thesunset 2022. 9. 28. 19:19

1. GROUP BY  : 그룹을 묶어줄 기준을 제시할 수 있는 구문

- 여러개의 값들을 그룹별로 나눠서 처리할 목적으로 사용

-- 전체 사원의 총 급여합
SELECT SUM(SALARY) FROM EMPLOYEE;

-- 각 부서별 총 급여합 
SELECT DEPT_CODE, SUM(SALARY) 
FROM EMPLOYEE
GROUP BY DEPT_CODE;

-- 전체 사원 수 
SELECT COUNT(*)
FROM EMPLOYEE;

-- 각 부서별 사원 수 
SELECT DEPT_CODE, COUNT(*)
FROM EMPLOYEE
GROUP BY DEPT_CODE;

-- 각 부서별 총 급여 합 부서별로 오름차순 정렬해서 조회
SELECT DEPT_CODE, SUM(SALARY)   --3. SELECT절
FROM EMPLOYEE       --1. FROM절 
GROUP BY DEPT_CODE  --2. GROUP BY절
ORDER BY DEPT_CODE; --4. ORDER BY절

-- 성별 별 사원 수 
SELECT DECODE(SUBSTR(EMP_NO, 8, 1), 1, '남자', 2, '여자') "성별", COUNT(*) 
FROM EMPLOYEE
GROUP BY SUBSTR(EMP_NO, 8, 1);
-- 부서별 평균 급여 300만원 이상인 부서들만 조회
SELECT DEPT_CODE, ROUND(AVG(SALARY))
FROM EMPLOYEE
--WHERE AVG(SALARY) >= 3000000
GROUP BY DEPT_CODE;
-->GROUP BY절이 WHERE절 보다 실행이 빠르기 때문 -> GROUP BY절과 WHERE절 함께 사용불가

1-2. HAVING : 그룹에 대한 조건을 제시하고자 할 때 사용되는 구문
    (대부분의 경우 그룹함수를 가지고 조건 제시)

SELECT DEPT_CODE, ROUND(AVG(SALARY))
FROM EMPLOYEE
GROUP BY DEPT_CODE
HAVING AVG(SALARY) >= 3000000;

--각 직급별 총 급여 합이 1000만원 이상인 직급코드, 급여합 조회
SELECT JOB_CODE, SUM(SALARY)
FROM EMPLOYEE
GROUP BY JOB_CODE
HAVING SUM(SALARY) >= 10000000;

--각 부서별 보너스를 받는 사원이 없는 부서만(DEPT_CODE)
SELECT DEPT_CODE
FROM EMPLOYEE
GROUP BY DEPT_CODE
HAVING COUNT(BONUS) = 0; -->HAVING절엔 그룹함수를 많이 씀

< 실행 순서 >
  5:  SELECT * / 조회하고자 하는 컬럼 / 산술연산 / 함수식 "별명"
  1:      FROM 조회하고자 하는 테이블
  2:  WHERE 조건식 
  3:  GROUP BY 그룹기준에 해당하는 컬럼명 / 함수식
  4:  HAVING  그룹함수식에 대한 조건식
  6:  ORDER BY 정렬기준에 대한 컬럼명 / 별칭 / 컬럼 순번 ASC, DESC NULL F/ NULL L

 SELECT EMP_ID "숫자", EMP_NAME
    FROM EMPLOYEE
 WHERE EMP_ID >= 215
 ORDER BY "숫자" DESC;

 

2. JOIN  : 두 개 이상의 테이블에서 데이터를 같이 조회하고자 할 때 사용되는 구문
    조회결과는 하나의 결과물(RESULT SET)로 나옴 

    - 관계형 데이터베이스에서는 최소한의 데이터로 각각의 테이블에서 데이터를 보관하고 있음
               -> 관계를 맺어본 적이 없음 
    - JOIN구문을 이용해서 여러 개의 테이블 간 "관계"를 맺어서 조회하는 법을 공부할 것임
    - 무작정 JOIN을 사용해서 조회하는 것이 아니라 테이블 간 "연결고리"에 해당하는 컬럼을 매칭시켜야 함.
    - JOIN은 크게 "오라클 전용 구문"과 "ANSI(미국 국립 표준협회) 구문"으로 나뉜다. 

오라클 전용 구문 ANSI(오라클 + 다른 DBMS) 구문
등가조인
(EQUL JOIN)
포괄조인 
(LEFT OUTER)
(RIGHT OUTER)
내부조인 (INNER JOIN)
외부조인 (OUTER JOIN)
왼쪽 외부조인 (LEFT OUTER JOIN)
오른쪽 외부조인 (RIGHT OUTER JOIN)
전체 외부조인 (FULL OUTER JOIN) => 오라클에선 불가
카테시안 곱(CARTESIAN PRODUCT) 교차조인(CROSS JOIN)
자체조인 (SELF JOIN)

 

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

: 연결시키는 컬럼의 값이 일치하는 행들만 조인돼서 조회 (==일치하지 않는 값들은 조회에서 제외)

 

* 오라클 전용구문 
- FROM절에 조회하고자 하는 테이블들을 나열(,)
- WHERE절에 매칭시킬 컬럼명(연결고리)에 대한 조건을 제시함

-- 전체 사원들의 사번, 사원명, 부서코드, 부서명을 같이 조회
--1) 연결할 두 컬럼명이 다른 경우
--EMPLOYEE = "DEPT_CODE" / DEPARTMENT = "DEPT_ID"

    SELECT DEPT_CODE FROM EMPLOYEE;
    SELECT DEPT_ID FROM DEPARTMENT;
    SELECT COUNT(*) FROM DEPARTMENT; 

    SELECT EMP_ID, EMP_NAME, DEPT_CODE, DEPT_TITLE
    FROM EMPLOYEE, DEPARTMENT; --> 23행 * 9행 = 207행 => 의도한 값이 아님(카테시안곱이 실행됨)
    
    SELECT EMP_ID, EMP_NAME, /*DEPT_CODE, DEPT_ID,*/ DEPT_TITLE
    FROM EMPLOYEE, DEPARTMENT
    WHERE DEPT_CODE = DEPT_ID; --> 연결고리 
--> 하지만, 부서코드가 NULL인 행들은 조회되지 않음.(일치하지 않기 때문)

-- 사번, 사원명, 직급코드, 직급명 
--2) 연결할 두 컬럼명이 같은 경우
    SELECT EMP_ID, EMP_NAME, JOB_CODE, JOB_NAME
        FROM EMPLOYEE, JOB
        WHERE JOB_CODE = JOB_CODE;
        --에러(ambiguously: 애매하다, 모호하다): 어떤 테이블의 컬럼인지 애매모호

--방법 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 구문 

- FROM절에 기준테이블 하나 기술
- 그 뒤에 JOIN절에 같이 조회하고자하는 테이블 기술(매치이킬 컬럼에 대한 조건도 기술)
- USING / ON

--사번, 사원명, 부서코드, 부서명 
--1. 연결할 두 컬럼명이 다른 경우
--EMPLOYEE - DEPT_CODE / DEPARTMENT - DEPT_ID
--무조건 ON구문만 사용함(USING은 사용 못함)

   SELECT EMP_ID, EMP_NAME, DEPT_CODE, DEPT_TITLE
    FROM EMPLOYEE
    JOIN DEPARTMENT ON (DEPT_CODE = DEPT_ID);
    
--사번, 사원명, 직급코드, 직급명
--2. 연결할 두 컬럼명이 같은 경우
-- EMPLOYEE - JOB_CODE / JOB - JOB_CODE
-- ON, USING구문
    
-- 2_1. ON 구문 이용 : AMBIGUOUSLY가 발생할 수 있기 때문에 명시!!!
SELECT EMP_ID, EMP_NAME, E.JOB_CODE, JOB_NAME
  FROM EMPLOYEE E
  JOIN JOB J ON(E.JOB_CODE = J.JOB_CODE);

--2_2.USING구문 이용: ambiguously 발생 X
SELECT EMP_ID, EMP_NAME, JOB_CODE, JOB_NAME
    FROM EMPLOYEE
    JOIN JOB USING(JOB_CODE);

 

 

+ NATURAL JOIN (자연조인)

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

-운좋게 두 개의 테이블에 일치하는 칼럼이 유일하게 딱 한 개 => 알아서 매칭됨 

 

* 추가적인 조건제시

--사번, 이름, 급여, 직급 이름 
--직급이 대리인 사원들의 정보만 조회
--> 오라클 전용 구문 
SELECT 
        EMP_ID, 
        EMP_NAME, 
        SALARY, 
        JOB_NAME 
FROM   
        EMPLOYEE E, 
        JOB J
WHERE  
        E.JOB_CODE = J.JOB_CODE 
AND     
        J.JOB_NAME = '대리';
--> 협업 시 가독성을 높이기 위해 보통 컬럼이나 조건 다 내려서 씀

--> ANSI구문

SELECT EMP_ID, EMP_NAME, SALARY, JOB_NAME 
FROM EMPLOYEE
JOIN JOB USING(JOB_CODE) 
WHERE JOB_NAME = '대리';

 

실습1. 부서가 '인사관리부'인 사원들의 사번, 사원명, 보너스를 조회

-->ORACLE
SELECT EMP_ID, EMP_NAME, BONUS
FROM EMPLOYEE
JOIN DEPARTMENT ON(DEPT_CODE = DEPT_ID)
WHERE DEPT_TITLE = '인사관리부';
-->ANSI
SELECT EMP_ID, EMP_NAME, BONUS
FROM EMPLOYEE, DEPARTMENT 
WHERE DEPT_CODE = DEPT_ID 
AND DEPT_TITLE = '인사관리부';

실습2. 부서가 '총무부'가 아닌 사원들의 사원명, 급여, 입사일을 조회

-->ORACLE
SELECT EMP_NAME, SALARY, HIRE_DATE
FROM EMPLOYEE
JOIN DEPARTMENT ON(DEPT_CODE = DEPT_ID)
WHERE DEPT_TITLE = '총무부';
-->ANSI
SELECT EMP_NAME, SALARY, HIRE_DATE
FROM EMPLOYEE, DEPARTMENT 
WHERE DEPT_CODE = DEPT_ID 
AND DEPT_TITLE = '총무부';

실습3. 보너스를 받는 사원들의 사번, 사원명, 보너스 부서명 조회

-->ORACLE
SELECT EMP_NO, EMP_NAME, BONUS
FROM EMPLOYEE
JOIN DEPARTMENT ON(DEPT_CODE = DEPT_ID)
WHERE BONUS IS NOT NULL;
-->ANSI
SELECT EMP_NO, EMP_NAME, BONUS
FROM EMPLOYEE, DEPARTMENT 
WHERE DEPT_CODE = DEPT_ID 
AND BONUS IS NOT NULL;

실습4. DEPARTMENT 테이믈, LOCATION테이블을 참고해서 부서코드, 부서명, 지역코드, 지역명(LOCAL_NAME) 조회

-->ORACLE
SELECT DEPT_ID, DEPT_TITLE, LOCAL_CODE, LOCAL_NAME
FROM DEPARTMENT, LOCATION
WHERE LOCAL_CODE = LOCATION_ID;
-->ANSI
SELECT DEPT_ID, DEPT_TITLE, LOCAL_CODE, LOCAL_NAME
FROM DEPARTMENT
JOIN LOCATION ON (LOCAL_CODE = LOCATION_ID);

* 등가조인 / 내부조인의 단점 : 등가조인 / 내부조인 : 일치하지 않는 행들은 애초에 조회되지 않음
-> 문제가 생길 여지가 있음 (모든 행을 조회하지 않는다)

 

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

테이블 간의 JOIN 시 일치하지 않는 행도 포함시켜서 조회 가능 

 단, 반드시 LEFT / RIGHT를 지정해야 함(기준이 되는 테이블 지정)  

 

전체 사원들의 사원명, 급여, 부서명 조회시,

             EMPLOYEE == DEPT_CODE가 NULL인 두 명의 사원도 조회한다면?

            DEPARTMENT == 부서에 배정된 사원이 없는 부서(D3, D4, D7)조회한다면?

 

1) LEFT (OUTER) JOIN : 두 테이블 중 왼편에 기술된 테이블을 기준으로 JOIN 

즉, 뭐가 되었든 간에 왼편에 기술된 테이블에 데이터는 무조건 조회(NULL) (일치하는 게 없어도 조회하겠다)

-->ANSI구문
SELECT EMP_NAME, SALARY, DEPT_TITLE
FROM EMPLOYEE
LEFT /*OUTER*/JOIN DEPARTMENT ON(DEPT_CODE = DEPT_ID);
--EMPLOYEE테이블을 기준으로 조회 했기 때문에 EMPLOYEE에 존재하는 데이터가 뭐든간에 무조건 조회되게끔 한다.
-->ORACLE 구문
SELECT EMP_NAME, SALARY, DEPT_TITLE
FROM EMPLOYEE, DEPARTMENT 
WHERE DEPT_CODE = DEPT_ID(+);
--내가 기준으로 삼을 테이블의 컬럼명 반대 항에 (+)라고 적기

 

2) RIGHT (OUTER) JOIN : 두 테이블의 오른편에 기술된 테이블을 기준으로 JOIN

-->ANSI
SELECT EMP_NAME, SALARY, DEPT_TITLE
FROM EMPLOYEE
RIGHT JOIN DEPARTMENT ON(DEPT_CODE = DEPT_ID);

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

3) FULL (OUTER) JOIN : 두 테이블이 가진 모든 행을 조회

--> ANSI
SELECT EMP_NAME, SALARY, DEPT_TITLE
FROM EMPLOYEE
FULL OUTER JOIN DEPARTMENT ON(DEPT_CODE = DEPT_ID); --> FULL 은 OUTER까지 적기

--> ORACLE (에러남 : ONLY ONE OUT-JOINED TABLE)
SELECT EMP_NAME, SALARY, DEPT_TITLE
FROM EMPLOYEE, DEPARTMENT 
WHERE DEPT_CODE(+) = DEPT_ID(+);

3. 카데시안 곱( CARTESIAN PRODUST) / 교차 조인(CROSS JOIN)

모든 테이블의 각 행들이 서로서로 매핑된 데이터가 조회됨(곱집합) 

두 테이블의 행들이 모두 곱해진 조합 출력
    => 방대한 데이터 출력
    => 과부화의 위험 

--사원명, 부서명
-->ORACLE 
SELECT EMP_NAME, DEPT_TITLE
    FROM EMPLOYEE, DEPARTMENT;
    
-->ANSI 
SELECT EMP_NAME, DEPT_TITLE
    FROM EMPLOYEE
    CROSS JOIN DEPARTMENT;

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

  '='를 사용하지 않는 조인문 

지정해주는 컬럼값이 일치하는 경우가 아닌 "범위"에 포함되는 경우 매칭 

--사원명, 급여
SELECT EMP_NAME, SALARY
FROM EMPLOYEE;

SELECT * FROM SAL_GRADE;
SELECT * FROM EMPLOYEE;

--사원명, 급여, 급여등급 (SAL_LEVEL)
-->ORACLE
SELECT EMP_NAME, SALARY, S.SAL_LEVEL
FROM EMPLOYEE E, SAL_GRADE S
WHERE SALARY BETWEEN MIN_SAL AND MAX_SAL;

-->ANSI(ON)
SELECT EMP_NAME, SALARY, S.SAL_LEVEL
FROM EMPLOYEE E
JOIN SAL_GRADE S ON (SALARY BETWEEN MIN_SAL AND MAX_SAL);

5.  자체조인(SELF JOIN)

같은 테이블을 다시 한 번 조인하는 경우 자기 자신의 테이블과 조인을 맺는다.

 

SELECT EMP_ID "사원 사번", EMP_NAME "사원명", SALARY "사원 급여", MANAGER_ID "사수사번"
FROM EMPLOYEE;

SELECT * FROM EMPLOYEE; --사원에 대한 정보 도출형 테이블
--MANAGER_ID
SELECT * FROM EMPLOYEE; --사수에 대한 정보 도출용 테이블
--EMP_ID

--사원사번, 사원명, 사원부서코드 , 사원급여
--사수사번, 사수명, 사수부서코드 , 사수급여
SELECT E.EMP_ID "사원번호", E.EMP_NAME "사원이름", E.DEPT_CODE "부서코드", E.SALARY "사원급여",
        M.EMP_ID "사수번호", M.EMP_NAME "사수이름", M.DEPT_CODE"부서코드", M.SALARY "사수급여"
FROM EMPLOYEE E, EMPLOYEE M
WHERE E.MANAGER_ID = M.EMP_ID(+) ; --> LEFT JOIN

--ANSI
SELECT E.EMP_ID "사원번호", E.EMP_NAME "사원이름", E.DEPT_CODE "부서코드", E.SALARY "사원급여",
        M.EMP_ID "사수번호", M.EMP_NAME "사수이름", M.DEPT_CODE"부서코드", M.SALARY "사수급여"
FROM EMPLOYEE E
LEFT JOIN EMPLOYEE M ON (E.MANAGER_ID = M.EMP_ID) ;

--사원사번, 사원명, 사원 부서코드 , 사원부서명, 사원 급여  
--사수사번, 사수명, 사수부서코드, 사수부서명, 사수 급여

SELECT E.EMP_ID "사원번호", E.EMP_NAME "사원이름", D1.DEPT_TITLE "부서명", E.SALARY "사원급여",
        M.EMP_ID "사수번호", M.EMP_NAME "사수이름", D2.DEPT_TITLE "부서명", M.SALARY "사수급여"
    FROM EMPLOYEE E
LEFT JOIN EMPLOYEE M ON(E.MANAGER_ID = M.EMP_ID)
 JOIN DEPARTMENT D1 ON(E.DEPT_CODE = D1.DEPT_ID)
 JOIN DEPARTMENT D2 ON(M.DEPT_CODE = D2.DEPT_ID);

6. 집합연산자 (SET OPERATOR)

    여러 개의 쿼리문을 가지고 하나의 쿼리문으로 만드는 연산자 
 
    - UNION : 합집합(두 쿼리문을 수행한 결과값을 더한 후 중복되는 부분은 한 번 뺀 것)

    - UNION ALL : 합집합+교집합 (두 쿼리문을 수행한 결과값을 무조건 더함, 즉 합집합에서 중복제거를 하지 않은것 )
                                =>  중복된 결과가 두 번 조회될 수 있음
    - INTERSECT : 교집합(두 쿼리문을 수행한 결과값의 중복된 결과값 부분) AND
    - MINUS : 차집합(선행 쿼리문 결과값 - 후행 쿼리문 결과값)

 

아래 식을 집합연산자를 통해 이용해보면?

--부서코드가 D5인 사원들만 조회
 SELECT EMP_ID, EMP_NAME, DEPT_CODE, SALARY
 FROM EMPLOYEE
 WHERE DEPT_CODE = 'D5'; --> 6명 (박나라, 하이유, 김해술, 심봉선, 윤은해, 대북혼)
    
-- 급여가 300만원 초과인 사원들만 조회
SELECT EMP_ID, EMP_NAME, DEPT_CODE, SALARY
FROM EMPLOYEE
WHERE SALARY > 3000000; --> 8명(선동일, 송종기, 노옹철, 유재식, 정중하, 심봉선, 대북혼, 전지연)

 

1) UNION( 합집합 - 두 쿼리문 수행한 결과값을 더하지만 중복되는 결과는 한 번만 조회 )

SELECT EMP_ID, EMP_NAME, DEPT_CODE, SALARY
 FROM EMPLOYEE
 WHERE DEPT_CODE = 'D5'
 
 UNION
 
 SELECT EMP_ID, EMP_NAME, DEPT_CODE, SALARY
FROM EMPLOYEE
WHERE SALARY > 3000000; --> 12명 조회 (6명 + 8명 -2명)
--두 쿼리의 SELECT절이 같아야 한다. 

SELECT EMP_ID, EMP_NAME, DEPT_CODE, SALARY
 FROM EMPLOYEE
 WHERE DEPT_CODE = 'D5' OR SALARY > 3000000;
--OR연산자로 두 개의 조건을 엮어서 조회하면 결과는 동일 

--각 부서별 급여합 조회(부서코드, 부서별 급여합)
--UNION을 이용해서 

SELECT SUM(SALARY)
FROM EMPLOYEE 
WHERE DEPT_CODE = 'D1'
UNION
SELECT SUM(SALARY)
FROM EMPLOYEE 
WHERE DEPT_CODE = 'D2'
UNION
SELECT SUM(SALARY)
FROM EMPLOYEE 
WHERE DEPT_CODE = 'D3'
UNION
SELECT SUM(SALARY)
FROM EMPLOYEE 
WHERE DEPT_CODE = 'D4';
--> 각 부서별로 WHERE 절을 이용해서 하나한 UNION으로 합쳐줘야 한다. 
--> GROUP BY를 이용해서 쉽게 해결 가능
SELECT DEPT_CODE, SUM(SALARY)
FROM EMPLOYEE 
GROUP BY DEPT_CODE;

 

2) UNION ALL: 여러개의 쿼리 결과를 무조건 더하는 연산자(중복값이 여러 개 들어갈 수 있음)
-> 다른 것들은 다른 방법으로 대체 가능하지만, UNION ALL은 불가능

--UNION ALL
SELECT EMP_ID, EMP_NAME, DEPT_CODE, SALARY
 FROM EMPLOYEE
 WHERE DEPT_CODE = 'D5'
 
 UNION ALL
 
 SELECT EMP_ID, EMP_NAME, DEPT_CODE, SALARY
FROM EMPLOYEE
WHERE SALARY > 3000000; --> 14명조회

3) INTERSECT (교집합 - 여러 쿼리 결과의 중복된 결과만을 조회) 

SELECT EMP_ID, EMP_NAME, DEPT_CODE, SALARY
 FROM EMPLOYEE
 WHERE DEPT_CODE = 'D5'
 
INTERSECT
 
 SELECT EMP_ID, EMP_NAME, DEPT_CODE, SALARY
FROM EMPLOYEE
WHERE SALARY > 3000000; --> 2명조회

SELECT EMP_ID, EMP_NAME, DEPT_CODE, SALARY
 FROM EMPLOYEE
 WHERE DEPT_CODE = 'D5' AND SALARY > 3000000;
 --> AND로 대체 가능

4) MINUS (차집합-선행쿼리 결과에 후행쿼리 결과를 뺀 나머지)

SELECT EMP_ID, EMP_NAME, DEPT_CODE, SALARY
 FROM EMPLOYEE
WHERE DEPT_CODE = 'D5' 
    MINUS
SELECT EMP_ID, EMP_NAME, DEPT_CODE, SALARY
 FROM EMPLOYEE
WHERE SALARY > 3000000; --> 4명조회

SELECT EMP_ID, EMP_NAME, DEPT_CODE, SALARY
 FROM EMPLOYEE
WHERE DEPT_CODE = 'D5' AND SALARY <= 3000000;
--> 부등호를 바꿔 가능

7. 다중 JOIN

-- 사번, 사원명, 부서명, 직급명

SELECT * FROM EMPLOYEE; -- DEPT_CODE       JOB_CODE
SELECT * FROM DEPARTMENT; --DEFT_ID        
SELECT * FROM JOB;        --               JOB_CODE

--ORACLE
SELECT EMP_ID, EMP_NAME, DEPT_TITLE, JOB_NAME
FROM EMPLOYEE
LEFT JOIN DEPARTMENT ON(DEPT_CODE = DEPT_ID)
JOIN JOB USING(JOB_CODE);

--ANSI
SELECT EMP_ID, EMP_NAME, DEPT_TITLE, JOB_NAME
FROM EMPLOYEE E, DEPARTMENT, JOB J
WHERE DEPT_CODE = DEPT_ID(+)
 AND E.JOB_CODE = J.JOB_CODE;
-- 사번, 사원명, 부서명, 직급명, 지역명
--ANSI
SELECT EMP_ID, EMP_NAME, DEPT_TITLE, LOCAL_NAME
FROM EMPLOYEE 
LEFT JOIN DEPARTMENT ON(DEPT_CODE = DEPT_ID)
LEFT JOIN JOB USING(JOB_CODE)
LEFT JOIN LOCATION ON(LOCATION_ID = LOCAL_CODE);
--ORACLE
SELECT EMP_ID, EMP_NAME, DEPT_TITLE, LOCAL_NAME
FROM EMPLOYEE E, DEPARTMENT, JOB J, LOCATION
WHERE DEPT_CODE = DEPT_ID(+) 
AND E.JOB_CODE = J.JOB_CODE
AND LOCATION_ID = LOCAL_CODE(+);
-- 다중 JOIN할 때 순서 중요(LOCATION 테이블이 DEPARTMENT테이블보다 먼저 조인되면 오류)
--사원명, 부서명, 직급명, 근무지역명, 근무국가명, 급여등급
--EMPLOYEE, DEPARTMENT, 

SELECT * FROM EMPLOYEE; --DEPT_CODE                 JOB_CODE            SALARY
SELECT * FROM DEPARTMENT;--DEPT_ID  LOCATION_ID
SELECT * FROM JOB;      --          LOCAL_CODE
SELECT * FROM LOCATION;--          NATIONAL_CODE         JOB_CODE
SELECT * FROM NATIONAL;--           NATIONAL_CODE
SELECT * FROM SAL_GRADE;  --                                            MIN_SAL, MAX_SAL


SELECT 
        E.EMP_ID "사번",  
        D.DEPT_TITLE "부서명", 
        J.JOB_NAME "직급명",
        L.LOCAL_NAME "근무지역명",
        N.NATIONAL_NAME "근무국가명",
        S.SAL_LEVEL "급여등급"
FROM
        EMPLOYEE E
JOIN    
        DEPARTMENT D ON(E.DEPT_CODE = D.DEPT_ID)
JOIN
        LOCATION L ON(D.LOCATION_ID = L.LOCAL_CODE)
JOIN   
        JOB J ON(E.JOB_CODE = J.JOB_CODE)
JOIN    
        NATIONAL N ON(L.NATIONAL_CODE = N.NATIONAL_CODE)
JOIN    
        SAL_GRADE S ON(E.SALARY BETWEEN MIN_SAL AND MAX_SAL);