본문 바로가기
Study/SQL

13.JOIN

by 로롤로롱 2023. 5. 5.

-- 테이블 이름 변경
RENAME DEPARTMENT TO DEPT;

/*
    <JOIN>
    - 두 개 이상의 테이블에서 데이터를 조회하고자 할 때 사용하는 구문
    - 조건을 주지 않으면 카티전 곱 발생 -> 행 * 행
    *카티전 곱 : 집합 A의 원소 a와 집합 B의 원소 b를 순서대로 벌여 놓고 괄호로 묶은 순서쌍 (a, b) 전체의 집합*
        1)등가조인(EQUAL JOIN) or 내부조인(INNER JOIN)
        - 연결시키는 칼럼의 값이 일치하는 행들만 조인돼서 조회함
          (일치하는 값이 없는 행은 조회X)
          (1)오라클 전용 구문
                SELECT 컬럼, 컬럼 ...
                FROM 테이블1, 테이블2
                WHERE 테이블1.컬럼명 = 테이블2.컬럼명;
                 
                - FROM절에 조회하고자 하는 컬럼들을 ,(콤마)로 구분하여 나열
                - WHERE절에 매칭시킬 컬럼에 대한 조건을 제시
          (2)ANSI 표준 구문
                SELECT 컬럼, 컬럼 ...
                FROM 테이블1
                [INNER] JOIN 테이블2 ON (테이블명1.컬럼명 = 테이블2.컬럼명);
                [INNER] JOIN 테이블2 USING (컬럼명);
                - FROM절에 기준이 되는 테이블을 기술
                - JOIN절에 같이 조회하고자 하는 테이블을 기술 후 조건을 명시
                - 연결에 사용하려는 컬럼명이 같은 경우 ON절 대신 USING(컬럼명)을 사용
                *ANSI SQL*
                  DMBS들에서 각기 다른SQL을 사용하므로, 미국 표준 협회에서 이를 표준화하여 표준SQL문을 정립 시켜 놓은 것
*/

-- 각 사원들의 사번, 사원명, 부서코드, 부서명을 조회
SELECT COUNT(*) FROM EMP; -- 23건
SELECT emp_id, emp_name, dept_code FROM emp;
SELECT COUNT(*) FROM dept; -- 9건
SELECT dept_id, dept_title FROM Dept;

-- 카티전 곱 emp 행23 * dept 행 9개 = 총 207개의 행이 결과로 나옴
SELECT *
FROM Emp, Dept;

-- 오라클 구문
-- 1)연결할 컬럼이 다른 경우
SELECT emp_id, emp_name, dept_id, dept_title
FROM Emp, Dept
WHERE dept_code = dept_id; -- 21건
-- *위 쿼리를 실행하고 나온 결과를 결과집합(ResultSet)이라고 함
-- INNER JOIN : 조인 조건에 만족하는 값만 조회
-- 사원 테이블에서 누락된 데이터 찾기
-- 전체 사원의 결과집합 - 조인문의 결과집합
-- emp테이블 제외 : dept_id에 null
SELECT *
FROM emp
MINUS
SELECT emp.* -- emp테이블이 가지고 있는 모든 컬럼을 조회
FROM Emp, Dept
WHERE dept_code = dept_id; -- JOIN 조건
 
-- 서브쿼리 이용해서 JOIN결과 중복을 제거 후 부서테이블에 사용되지 않은 부서코드 찾기
SELECT *
FROM dept
WHERE dept_id NOT IN(SELECT DISTINCT(dept_id)
                    FROM Emp, dept
                    WHERE dept_code = dept_id
);

-- ANSI 표준 구문 사용
SELECT emp_id, emp_name, dept_code, dept_title
FROM Emp
JOIN Dept ON(dept_code = dept_id);

-- 각 사원들의 사번, 사원명, 직급코드, 직급명을 조회
-- ANSI 구문 USING
-- USING을 사용할 경우 테이블명.컬럼명 사용 불가(식별자 사용X)
SELECT emp_id, emp_name, job_code, job_name
FROM Emp
JOIN Job USING(job_code);
-- ANSI 구문 ON
SELECT emp_id, emp_name, Emp.job_code, job_name
FROM Emp
JOIN Job ON(Emp.job_code = Job.job_code);
-- 오라클 구문 *컬럼명이 동일한 경우 테이블명을 명시해야 한다. 그렇지 않으면 에러(열의 정의가 애매합니다)
-- 테이블에 별칭을 사용한 경우 SELECT절에서 별칭을 이용해서 접근해야함
SELECT emp_id, emp_name, E.job_code, job_name
FROM Emp E, Job J
WHERE E.job_code = J.job_code;

-- emp 테이블과 job 테이블을 조인하여 직급이 대리인 사원의 사번,사원명,직급명 급여를 조회
SELECT emp_id, job_name, emp_name, salary
FROM Emp E, Job J
WHERE E.job_code = J.job_code AND job_name = '대리';
-- ANSI ON
SELECT emp_id, job_name, emp_name, salary
FROM Emp
JOIN Job ON(Emp.job_code = Job.job_code)
WHERE job_name = '대리';
-- ANSI USING
SELECT emp_id, job_name, emp_name, salary
FROM Emp
JOIN Job USING(job_code)
WHERE job_name = '대리';

-- 외부조인
SELECT emp_id, emp_name, dept_id, dept_title
FROM Emp, Dept
WHERE dept_code = dept_id(+);

SELECT emp_id, emp_name, dept_id, dept_title
FROM Emp, Dept
WHERE dept_code(+) = dept_id;

/*
    2. 다중JOIN
        - 여러개의 테이블을 조인하는 경우 사용
*/
-- 사번, 사원명, 부서명, 지역명 조회
-- 오라클 구문
-- 다중조인 순서 상관 없음
SELECT emp_id, emp_name, dept_title, local_name
FROM emp, dept, location
WHERE dept_id=dept_code AND location_id=local_code;
-- ANSI(JOIN 두번)
-- 다중조인의 경우 순서 주의
SELECT emp_id, emp_name, dept_title, local_name
FROM emp
INNER JOIN dept ON(dept_id=dept_code) -- INNER조인 생략 가능
JOIN location ON(location_id=local_code); -- 아무것도 안쓰면 INNER조인 

/*
    3. 외부조인(OUTER JOIN)
        - 테이블간에 JOIN시 조건에 일치하지 않는 행도 포함시켜서 조회
        - 기준이 되는 테이블을 지정해야 함(LEFT/LIGHT/(+))
        - ANSI : LEFT/RIGHT
        - 오라클 : (+)
*/
-- 1)LEFT [OUTER] JOIN
--      두 테이블 중 왼편에 기술된 테이블의 컬럼을 기준으로 JOIN을 진행
--      JOIN조건이 일치하지 않아도 왼쪽테이블을 모두 출력
-- emp테이블의 dept_code가 null인 행을 출력하고 싶으면 다른 쪽 테이블에 (+)
SELECT emp_id, emp_name, dept_id, dept_title
FROM emp, dept
WHERE emp.dept_code = dept.dept_id(+);
-- ANSI
SELECT emp_id, emp_name, dept_id, dept_title
FROM emp
LEFT OUTER JOIN dept ON (dept_code = dept_id) ; -- OUTER 생략 가능

-- 2)RIGHT [OUTER] JOIN
--      두 테이블 중 오른편에 기술된 테이블의 컬럼을 기준으로 JOIN을 진행
--      오른쪽 테이블을 모두 출력
-- 사원이름, 부서명, 급여를 출력하는데 부서 테이블의 모든 데이터가 출력되도록
-- ANSI
SELECT emp_name, dept_title, salary
FROM emp
RIGHT JOIN dept ON(dept_code = dept_id);
--ORACLE
SELECT emp_name, dept_title, salary
FROM emp, dept
WHERE dept_code(+) = dept_id;

-- 3)FULL [OUTER] JOIN : 두 테이블이 가지는 모든 행을 조회
--      오라클 구문은 지원하지 않음
-- 21건 조회조건 만족, 부서코드가 NULL인 2건, 사원이 배정되지않은 부서코드 3건 총 26건
SELECT emp_name, dept_title, salary
FROM emp
FULL JOIN dept ON(dept_code = dept_id);

 

/*
5. 비등가 조인(NON EQUAL JOIN) 
    - 조인 조건에 등호(=)를 사용하지 않는 조인문.
      지정한 컬럼값이 일치하는 경우가 아닌, 값의 범위에 포함되는 행들을 연결하는 방식.
      (비교 연산자 >, <, >=, <=, BETWEEN AND, IN, NOT IN 등을 사용)
*/
-- 급여등급 테이블
CREATE TABLE SAL_GRADE (
    SAL_LEVEL CHAR(2),
    MIN_SAL NUMBER,
    MAX_SAL NUMBER
);

COMMENT ON COLUMN SAL_GRADE.SAL_LEVEL IS '급여등급';
COMMENT ON COLUMN SAL_GRADE.MIN_SAL IS '최소급여';
COMMENT ON COLUMN SAL_GRADE.MAX_SAL IS '최대급여';
COMMENT ON TABLE SAL_GRADE IS '급여등급';

INSERT INTO sal_grade VALUES ('S1', 6000000, 10000000);
INSERT INTO sal_grade VALUES ('S2', 5000000, 5999999);
INSERT INTO sal_grade VALUES ('S3', 4000000, 4999999);
INSERT INTO sal_grade VALUES ('S4', 3000000, 3999999);
INSERT INTO sal_grade VALUES ('S5', 2000000, 2999999);
INSERT INTO sal_grade VALUES ('S6', 1000000, 1999999);

COMMIT;
SELECT * FROM SAL_GRADE;

-- EMP 테이블과 SAL_GRADE 테이블을 비등가 조인하여 
-- 사원명, 급여, 급여 등급 조회
-- 오라클 구문
SELECT  EMP_NAME, SALARY, SAL_LEVEL
FROM    EMP, SAL_GRADE
WHERE   SALARY BETWEEN MIN_SAL AND MAX_SAL
ORDER BY EMP_ID;

-- 범위가 중복되는 경우 사원이 여러 급여등급을 가지게 되어 중복되어 출력
UPDATE SAL_GRADE SET MAX_SAL = 2999999 WHERE SAL_LEVEL = 'S5';

-- ANSI
SELECT  EMP_NAME, SALARY, SAL_LEVEL
FROM    EMP
JOIN    SAL_GRADE ON (SALARY BETWEEN MIN_SAL AND MAX_SAL);

/*
6. 자체 조인(SELF JOIN)
    같은 테이블을 다시 한번 조인하는 경우에 사용한다.
*/
--  MANAGER_ID : 사수의 사번
-- 사번, 사원명, 부서 코드, 사수 사번, 사수 이름
SELECT  E.EMP_ID 사번, E.EMP_NAME 사원명
        , E.DEPT_CODE 부서코드, E.MANAGER_ID 매니져번호
        , M.EMP_ID 매니져번호M, M.EMP_NAME 매니져이름M
FROM    EMP E, EMP M
WHERE   E.MANAGER_ID = M.EMP_ID(+);

-- ANSI 구문
SELECT  E.EMP_ID 사번, E.EMP_NAME 사원명
        , E.DEPT_CODE 부서코드, E.MANAGER_ID 매니져번호
        , M.EMP_ID 매니져번호M, M.EMP_NAME 매니져이름M
FROM    EMP E 
LEFT /*OUTER*/ JOIN    EMP M ON (E.MANAGER_ID = M.EMP_ID);

-- 실습문제
-- 보너스를 받는 사원들의 사번, 사원명, 보너스, 부서명을 조회
-- 오라클
SELECT  EMP_ID, EMP_NAME, BONUS, DEPT_TITLE
FROM    EMP, DEPT
WHERE   DEPT_CODE = DEPT_ID
AND     BONUS IS NOT NULL;
-- ANSI
SELECT  EMP_ID, EMP_NAME, BONUS, DEPT_TITLE
FROM    EMP
JOIN    DEPT ON (DEPT_CODE=DEPT_ID)
WHERE   BONUS IS NOT NULL;

-- 인사관리부가 아닌 사원들의 사원명, 부서명, 급여를 조회
SELECT  EMP_NAME, DEPT_TITLE, SALARY
FROM    EMP, DEPT
WHERE   EMP.DEPT_CODE = DEPT.DEPT_ID(+)
AND     DEPT_TITLE != '인사관리부';

-- ANSI
SELECT  EMP_NAME, NVL(DEPT_TITLE,'부서없음'), DEPT_CODE, SALARY
FROM    EMP 
LEFT OUTER JOIN    DEPT ON (DEPT_CODE = DEPT_ID)
WHERE NVL(DEPT_TITLE,'부서없음') != '인사관리부';

-- 사번, 사원명, 부서명, 지역명, 국가명 조회
-- 누락되는 사원 없이 조회
SELECT e.emp_id, e.emp_name, d.dept_title, l.local_name, n.national_name
FROM Emp e, Dept d,Location l, National n
WHERE e.dept_code=d.dept_id(+)
AND d.location_id=l.local_code(+)
AND l.national_code=n.national_code(+);

-- ANSI
SELECT e.emp_id, e.emp_name, d.dept_title, l.local_name, n.national_name
FROM Emp e
LEFT JOIN Dept d ON(dept_code=dept_id)
LEFT JOIN Location l ON(location_id=local_code)
LEFT JOIN National n ON(l.national_code=n.national_code);

-- 사번, 사원명, 부서명, 지역명, 국가명, 급여 등급 조회
SELECT e.emp_id, e.emp_name, d.dept_title, l.local_name, s.sal_level
FROM Emp e, Dept d, Location l, Sal_Grade s
WHERE e.dept_code = d.dept_id(+)
AND d.location_id = l.local_code(+)
AND salary BETWEEN min_sal AND max_sal(+);

-- 종합실습문제
-- 1. 직급이 대리이면서 ASIA 지역에서 근무하는 직원들의 사번, 사원명, 직급명, 부서명, 근무지역, 급여 조회
SELECT e.emp_id, e.emp_name, j.job_name, d.dept_title, l.local_name, e.salary
FROM Emp e, Job j, Dept d, Location l
WHERE e.job_code = j.job_code
AND e.dept_code = d.dept_id
AND d.location_id = l.local_code
AND j.job_name = '대리' 
AND l.local_name LIKE 'ASIA%';

/*
2) CASE
    CASE WHEN 조건식1 THEN 결과값1
         WHEN 조건식2 THEN 결과값2
         ...
         ELSE 결과값
    END
*/
-- 2. 70년대생 이면서 여자이고, 성이 전씨인 직원들의 사원명, 주민번호, 부서명, 직급명
SELECT emp_name AS 사원, emp_no AS 주민번호, dept_title AS 부서, job_name AS 직급
FROM Emp
LEFT JOIN Dept ON(dept_code = dept_id)
JOIN Job USING(job_code)
--WHERE DECODE(SUBSTR(emp_no,8,1), '1', '남', '3', '남', '2', '여', '4', '여', '확인')='여'
WHERE (SUBSTR(emp_no,8,1)='2' OR SUBSTR(emp_no,8,1)='4')
AND emp_name LIKE '전%'
AND SUBSTR(emp_no,1,1) = '7';

-- 3. 보너스를 받는 직원들의 사원명, 보너스, 연봉, 부서명, 근무지역을 조회
-- 단, 부서코드가 없는 사원도 출려고딜 수 있게 OUTER JOIN 사용
-- 연봉 : 월급 *12 세자리콤마 표시
SELECT emp_name, NVL(bonus, 0) AS 보너스, TO_CHAR(salary*12,'999,999,999') AS 연봉, dept_title, local_name
FROM Emp
LEFT JOIN Dept ON(dept_code = dept_id)
LEFT JOIN Location ON(location_id = local_code)
--WHERE NVL(bonus, 0) != 0;
WHERE bonus IS NOT NULL;

-- 4. 한국과 일본에서 근무하는 직원들의 사원명, 부서명, 근무지역, 근무국가를 조회
SELECT emp_name, dept_title, local_name, national_name
FROM Emp
JOIN Dept ON(dept_code = dept_id)
JOIN Location ON(location_id = local_code)
JOIN National USING(national_code)
--WHERE  national_code='KO' OR  national_code='JP';
WHERE  national_name IN('한국', '일본');

-- 5. 각 부서별 평균 급여를 조회하여 부서명, 평균급여(정수처리)를 조회
-- 단 부서배치가 안된 사원들의 평균도 같이 나오도록
SELECT NVL(dept_title, '부서없음') AS 부서명, TO_CHAR(ROUND(AVG(salary)), '999,999,999') AS 평균급여
FROM Emp, Dept
WHERE dept_code = dept_id(+)
GROUP BY dept_title;

-- 6. 각 부서별 총 급여의 합이 1000만원이상인 부서의 부서명, 급여의 합
SELECT NVL(dept_title, '부서없음') AS 부서명, TO_CHAR(ROUND(SUM(salary)), '999,999,999') AS 총급여
FROM Emp, Dept
WHERE dept_code = dept_id(+)
HAVING SUM(salary)>=10000000
GROUP BY dept_title;

-- 7. 사번, 사원명, 직급명, 급여등급, 구분을 조회
SELECT emp_id, emp_name, job_name, sal_level,
    CASE 
        WHEN sal_level='S1' OR sal_level='S2' THEN '고급'
        WHEN sal_level IN('S3', 'S4') THEN '중급'
        ELSE '초급'
    END AS 구분
FROM Emp e, Job j, sal_grade s
WHERE e.job_code = j.job_code
AND salary BETWEEN min_sal AND max_sal;

-- 8. 보너스를 받지 않는 직원들 중 직급코드가 J4,J7인 직원들의 사원명, 직급명, 급여 조회
SELECT emp_name, job_name, salary
FROM Emp e, Job j
WHERE e.job_code = j.job_code
AND bonus IS NULL
AND e.job_code IN('J4', 'J7');

-- 9. 부서가 있는 직원들의 사원명, 직급명, 부서명, 근무지역 조회
SELECT emp_name, job_name, dept_title, local_name
FROM Emp
JOIN Dept ON(dept_code = dept_id)
JOIN Job USING(job_code)
JOIN Location ON(location_id = local_code);

-- 10. 해외영업팀에 근무하는 직원들중 2013-01-01 이후 입사자의 사원명, 직급명, 부서코드, 부서명 조회
SELECT emp_name, job_name, dept_id, dept_title
FROM Emp
JOIN Dept ON(dept_code = dept_id)
JOIN Job USING(job_code)
JOIN Location ON(location_id = local_code)
WHERE dept_title LIKE '해외영업%'
AND hire_date>='2013/01/01';

-- 11. 이름에 '형'자가 들어있는 직원들의 사번, 사원명, 직급명
SELECT emp_id, emp_name, job_name
FROM Emp e, Job j
WHERE e.job_code = j.job_code
AND emp_name LIKE '%형%';

'Study > SQL' 카테고리의 다른 글

21.인덱스  (0) 2023.05.13
16.DML  (0) 2023.05.08
12.기본키,외래키  (1) 2023.05.04
10.DDL  (0) 2023.05.02
9.그룹함수  (0) 2023.05.01

댓글