set 연산사(집합 연산자) - UNION, UNION ALL, INTERSECT, MINUS, 다중컬럼 서브쿼리, 스칼라 서브쿼리, 상호연관 서브쿼리

2019. 4. 18. 10:56DateBase

-----------------------------Set 연산자 (집합 연산자)----------------------------------

set 연산자 (집합 연산자)
            - 두 개 이상의 조회 결과를 하나로 결합한다.
            - 연산자
                        UNION
                                    A UNION B
                                    A의 조회결과와 B의 조회결과에 포함된 모든 행을 반환한다.
                                    단, A와 B 둘 다에 포함되어 있는 행은 한번만 선택된다.
                                    (A와 B의 합집합을 반환한다. 단, 중복된 행은 한번만)
                        UNION ALL
                                    A UNION ALL B
                                    A의 조회결과와 B의 조회결과에 포함된 모든 행을 반환한다.
                                    A와 B 모두에 포함된 중복된 행도 모두 반환한다.
                                    (A와 B의 합집합을 반환한다. 중복된 행 포함)
                        INTERSECT
                                    A INTERSECT B
                                    A의 조회결과와 B의 조회결과에 공통으로 포함된 행만 반환한다.
                                    (A와 B의 교집합을 반환한다, 교집합인 경우 조인도 가능하다.)
                        MINUS
                                    A MINUS B
                                    A의 조회결과에서 B의 조회결과를 뺀 행을 반환한다.
                                    (A와 B의 차집합을 반환한다)


            - 쿼리 작성 시 주의 사항
                        - A쿼리문과 B쿼리문의 선택된 컬럼의 갯수가 동일해야 한다.
                                    SELECT 컬럼1, 컬럼2
                                    FROM A
                                    SELECT 컬럼3, 컬럼4
                                    FROM B

                        - A쿼리문과 B쿼리문에서 선택된 컬럼의 데이터 타입이 동일해야 한다. 
                                    * 컬럼1, 컬럼3이 타입이 같아야하고 컬럼2와 컬럼4가 타입이 같아야한다.

                        - A쿼리문과 B쿼리문에서 선택된 컬럼의 이름을 동일하지 않아도 된다.
                        - 정렬을 위한 ORDER BY 절은 맨 마지막 SELECT문에 붙인다.     

            - 다중컬럼 서브쿼리
                        - 서브쿼리가 두 개 이상의 컬럼을 반환하는 경우 사용된다.
                        - SELECT ~
                           FROM ~
                           WHERE (컬럼1, 컬럼2) IN ( SELECT 컬럼1, 컬럼2
                                                              FROM ~
                                                              WHERE ~ )
                        * 컬럼1끼리, 컬럼2끼리 실제 컬럼명은 다를 수 있다.

            - 스칼라 서브쿼리
                        - 하나의 행에서 하나의 값(1행 1열)만 반환하는 서브쿼리다.
                        - 오직 하나의 값을 반환하는 서브쿼리다.
                        - 주로 SELECT절에서 사용된다.
                           * DECODE 및 CASE의 조건 및 표현식 부분
                           * SELECT 문의 모든 절에서 사용가능하다. (GROUP BY 절은 제외)
                           * UPDATE문의 SET절

            - 상호연관 서브쿼리
                        - SELECT COLUMN1, COLUMN2,
                                    (SELECT COLUM1
                                     FROM TABLE INNER
                                      WHERE INNER.COLUM3 = OUTER.COLUM3)
                          FROM TABLE OUTER
                        - SELECT COLUM1, COLUM2, ...
                          FROM TABLE OUTER
                          WHERE COLUM3 = (SELECT COLUM1
                                                            FROM INNER
                                                            WHERE INNER.COLUMN4 = OUTER.COLUMN4)

                        - 상호연관 서브쿼리는 OUTER 쿼리의 행이 조회될 때마다 서브(INNER)쿼리가 실행된다.



예시) 모든 직원의 현재 직종 및 이전 직종을 사원아이디, 직종아이디로 표현하기

           1) 각 사원에 대해 동일한 직종은 한번만 조회

            SELECT EMPLOYEE_ID, JOB_ID  -- 현재
            FROM EMPLOYEES
            UNION
            SELECT EMPLOYEE_ID, JOB_ID -- 과거
            FROM JOB_HISTORY;





           2) 모든 직원의 현재 직종 및 이전 직종 표현하기

           SELECT EMPLOYEE_ID, JOB_ID  -- 현재
           FROM EMPLOYEES
           UNION ALL
           SELECT EMPLOYEE_ID, JOB_ID -- 과거
           FROM JOB_HISTORY
           ORDER BY EMPLOYEE_ID;





예시2) 50번 부서에서 근무중이거나 근무했던 사원의 아이디, 직종을 조회하기

           SELECT EMPLOYEE_ID, JOB_ID, HIRE_DATE, NULL  -- 값이 없는경우 NULL을 입력하면 실행된다.
           FROM EMPLOYEES
           WHERE DEPARTMENT_ID =50
           UNION ALL
           SELECT EMPLOYEE_ID, JOB_ID, START_DATE, END_DATE
           FROM JOB_HISTORY
           WHERE DEPARTMENT_ID = 50;





예시3) 현재 50번 주서에 근무중이 직원중에서 이전에 50번 부서 근무 경험이 있는 직원 조회하기

           SELECT B.FIRST_NAME, A.EMPLOYEE_ID
           FROM (SELECT EMPLOYEE_ID
                      FROM EMPLOYEES
                      WHERE DEPARTMENT_ID = 50
                      INTERSECT
                      SELECT EMPLOYEE_ID
                      FROM JOB_HISTORY
                      WHERE DEPARTMENT_ID = 50) A, EMPLOYEES B
           WHERE A.EMPLOYEE_ID = B.EMPLOYEE_ID;





예시) 90번 부서에 근무중인 사원중에서 업무가 변경된 적이 한번도 없는 사원의 아이디를 조회하기

           SELECT EMPLOYEE_ID
           FROM EMPLOYEES
           WHERE DEPARTMENT_ID = 90
           MINUS
           SELECT EMPLOYEE_ID
           FROM JOB_HISTORY;






///////////////////////////////////다중컬럼 서브쿼리

예시) 직원아이디가 174 사원의 관리자 및 부서가 동일한 사원을 조회하기

           SELECT *
           FROM EMPLOYEES
           WHERE (MANAGER_ID, DEPARTMENT_ID) IN (SELECT MANAGER_ID, DEPARTMENT_ID
                                                                             FROM EMPLOYEES
                                                                             WHERE EMPLOYEE_ID = 174);





예시2) 부서별 최고급여를 조회했을때 그 급여를 받는 사원 조회

           SELECT EMPLOYEE_ID, FIRST_NAME, NVL(TO_CHAR(DEPARTMENT_ID),'없음'), SALARY
           FROM EMPLOYEES
           WHERE (NVL(DEPARTMENT_ID,0), SALARY) IN (SELECT NVL(DEPARTMENT_ID,0), MAX(SALARY)
                                                                        FROM EMPLOYEES
                                                                        GROUP BY DEPARTMENT_ID);





///////////////////////////////////스칼라 서브쿼리

예시) 아이디, 이름, 급여, 평균급여와의 차

           SELECT EMPLOYEE_ID, FIRST_NAME, SALARY-(SELECT TRUNC(AVG(SALARY)) FROM EMPLOYEES) 평균급여차
           FROM EMPLOYEES;





예시2) 평균급여보다 적은 급여를 받는 사원의 급여를 인상시키기
         (평균급여 - 급여)/4

           UPDATE EMPLOYEES
           SET SALARY = SALARY + TRUNC(((SELECT AVG(SALARY) FROM EMPLOYEES) - SALARY/4))
           WHERE EMPLOYEE_ID IN (SELECT EMPLOYEE_ID
                                            FROM EMPLOYEES
                                            WHERE SALARY <(SELECT AVG(SALARY)
                                            FROM EMPLOYEES));





예시3) 급여별 급여 인상액 추청해보기
평균급여 대비 15000달러 이상 10%인상, 10000달러 이상 15%인상, 10000달러 미만 20%인상


           SELECT 
                      EMPLOYEE_ID,
                      FIRST_NAME,
                      SALARY,
                      CASE
                                 WHEN SALARY >= 15000 THEN TRUNC((SELECT AVG(SALARY) FROM EMPLOYEES) * 0.1)
                                 WHEN SALARY >= 10000 THEN TRUNC((SELECT AVG(SALARY) FROM EMPLOYEES) * 0.15)
                                 ELSE TRUNC((SELECT AVG(SALARY) FROM EMPLOYEES) * 0.2)
                      END INCREMENT_SALARY
           FROM EMPLOYEES;







///////////////////////////////////////////상호연관 서브쿼리

예시) 부서 아이디, 부서이름, 부서별 사원수 계산하기

           SELECT D.DEPARTMENT_ID, D.DEPARTMENT_NAME,
                      (SELECT COUNT(*) FROM EMPLOYEES X WHERE X.DEPARTMENT_ID = D.DEPARTMENT_ID) 사원수
           FROM DEPARTMENTS D
           WHERE D.MANAGER_ID IS NOT NULL;





예시2) 일반 서브쿼리와 상호연관 서브쿼리
            -- 전체 사원의 평균급여보다 급여를 많이 받는 사원 조회하기
            일반서브쿼리

           SELECT FIRST_NAME, SALARY
           FROM EMPLOYEES
           WHERE SALARY > (SELECT AVG(SALARY)
                                    FROM EMPLOYEES);  -- 서브쿼리는 단 한번 실행되고, 실행결과가 조건식에 사용된다.




           -- 소속부서의 평균급여보다 급여를 많이 받는 사원 조회하기
            상호연관 서브쿼리

           SELECT E.FIRST_NAME, E.SALARY
           FROM EMPLOYEES E
           WHERE E.SALARY > (SELECT AVG(B.SALARY)
                                      FROM EMPLOYEES B
                                      WHERE B.DEPARTMENT_ID = E.DEPARTMENT_ID);