본문 바로가기
Programming/DB

[Oracle] subquery

by AI_Wooah 2022. 3. 7.
-- SUBQUERY(서브쿼리)
-- 서브쿼리 : 쿼리문 안에 사용된 쿼리문

-- 사원명이 노옹철인 사람의 부서 조회
SELECT 
       DEPT_CODE
  FROM EMPLOYEE
 WHERE EMP_NAME = '노옹철';

-- 부서코드가 D9인 직원을 조회
SELECT
       EMP_NAME
  FROM EMPLOYEE
 WHERE DEPT_CODE = 'D9';

-- 부서코드가 노옹철 사원과 같은 소속의 직원 명단 조회
SELECT
       EMP_NAME
  FROM EMPLOYEE
 WHERE DEPT_CODE = (SELECT DEPT_CODE
                      FROM EMPLOYEE
                     WHERE EMP_NAME = '노옹철');

-- 전 직원의 평균 급여보다 많은 급여를 받고 있는 직원의
-- 사번, 이름, 직급코드, 급여를 조회하세요
SELECT AVG(SALARY)
  FROM EMPLOYEE;

SELECT
       EMP_ID
     , EMP_NAME
     , JOB_CODE
     , SALARY
  FROM EMPLOYEE
 WHERE SALARY >= (SELECT AVG(SALARY)
                    FROM EMPLOYEE);
                    
-- 서브쿼리의 유형
-- 단일행 서브쿼리 : 서브쿼리의 조회 결과값이 1개 행일 때
-- 다중행 서브쿼리 : 서브쿼리의 조회 결과값의 행이 여러 개 일때
-- 다중열 서브쿼리 : 서브쿼리의 조회 결과값의 컬럼이 여러 개 일때
-- 다중행 다중열 서브쿼리 : 조회 결과 행 수와 열 수가 여러 개 일때

-- 서브쿼리의 유형에 따라 서브쿼리 앞에 붙는 연산자가 다름
-- 단일행 서브쿼리는 앞에 일반 비교연산자가 사용됨
-- >, <, >=, <=, =, !=/<>/^=

-- 노옹철 사원의 급여보다 많이 받는 직원의
-- 사번, 이름, 부서, 직급, 급여를 조회하세요
SELECT SALARY
  FROM EMPLOYEE
 WHERE EMP_NAME = '노옹철';

SELECT
       EMP_ID
     , EMP_NAME
     , DEPT_CODE
     , JOB_CODE
     , SALARY
  FROM EMPLOYEE
 WHERE SALARY > (SELECT SALARY
                   FROM EMPLOYEE
                  WHERE EMP_NAME = '노옹철');

-- 가장 적은 급여를 받는 직원의
-- 사번, 이름, 직급, 부서, 급여, 입사일을 조회하세요
SELECT MIN(SALARY)
  FROM EMPLOYEE;

SELECT 
       EMP_ID
     , EMP_NAME
     , DEPT_CODE
     , JOB_CODE
     , SALARY
     , HIRE_DATE
  FROM EMPLOYEE
 WHERE SALARY = (SELECT MIN(SALARY)
                   FROM EMPLOYEE);

-- 서브쿼리는 SELECT, FROM, WHERE, HAVING, ORDER BY 절에서도 사용할 수 있다.

-- 부서별 급여의 합계가 가장 큰 부서의 부서명, 급여 합계를 구하세요
SELECT
       DEPT_TITLE
     , SUM(SALARY)
  FROM EMPLOYEE
  LEFT JOIN DEPARTMENT ON(DEPT_CODE = DEPT_ID)
 GROUP BY DEPT_TITLE
 HAVING SUM(SALARY) = (SELECT MAX(SUM(SALARY))
                         FROM EMPLOYEE
                        GROUP BY DEPT_CODE);

-- 다중행 서브쿼리
-- 다중행 서브쿼리 앞에서는 일반 비교 연산자 사용 못함
-- IN / NOT IN : 여러 개의 결과값 중에서 한 개라도 일치하는 값이 있다면
--               혹은 없다면 이라는 의미
-- > ANY, < ANY : 여러 개의 결과값 중에서 한 개라도 큰 / 작은 경우
--                가장 작은 값 보다 크냐? / 가장 큰 값 보다 작냐?
-- > ALL, < ALL : 모든 값 보다 큰 / 작은 경우
--                가장 큰 값보다 크냐? / 가장 작은 값보다 작냐?
-- EXISTS / NOT EXISTS : 값이 존재하냐? / 존재하지 않냐?

-- 부서별 최고 급여를 받는 직원의 이름, 직급, 부서, 급여 조회
SELECT MAX(SALARY)
  FROM EMPLOYEE
 GROUP BY DEPT_CODE;

SELECT
       EMP_NAME
     , JOB_CODE
     , DEPT_CODE
     , SALARY
  FROM EMPLOYEE
 WHERE SALARY IN (SELECT MAX(SALARY)
                    FROM EMPLOYEE
                   GROUP BY DEPT_CODE);

-- SELECT 절에서도 서브쿼리를 이용할 수 있다.
SELECT
       EMP_ID
     , EMP_NAME
     , DEPT_TITLE
     , CASE
         WHEN EMP_ID IN (SELECT DISTINCT MANAGER_ID
                           FROM EMPLOYEE
                          WHERE MANAGER_ID IS NOT NULL) THEN '관리자'
         ELSE '직원'
       END AS 구분
  FROM EMPLOYEE
  LEFT JOIN DEPARTMENT ON (DEPT_CODE = DEPT_ID);

-- 대리 직급의 직원들 중에서 과장 직급의 최소 급여보다 많이 받는 직원의
-- 사번, 이름, 직급, 급여를 조회하세요
-- 단, > ANY OR < ANY 연산자 사용
SELECT EMP_ID, EMP_NAME, JOB_NAME, SALARY
  FROM EMPLOYEE E
  JOIN JOB J ON (E.JOB_CODE = J.JOB_CODE)
 WHERE JOB_NAME = '과장';

SELECT EMP_ID, EMP_NAME, JOB_NAME, SALARY
  FROM EMPLOYEE E
  JOIN JOB J ON (E.JOB_CODE = J.JOB_CODE)
 WHERE JOB_NAME = '대리';

SELECT EMP_ID, EMP_NAME, JOB_NAME, SALARY
  FROM EMPLOYEE E
  JOIN JOB J ON (E.JOB_CODE = J.JOB_CODE)
 WHERE JOB_NAME = '대리'
   AND SALARY > ANY (SELECT SALARY
                       FROM EMPLOYEE E
                       JOIN JOB J ON (E.JOB_CODE = J.JOB_CODE)
                      WHERE JOB_NAME = '과장');

-- 차장 직급의 급여의 가장 큰 값보다 많이 받는 과장 직급의 
-- 사번, 이름, 직급, 급여를 조회하세요
-- 단, > ALL 혹은 < ALL 연산자를 사용
SELECT EMP_ID, EMP_NAME, JOB_NAME, SALARY
  FROM EMPLOYEE
  JOIN JOB USING(JOB_CODE)
 WHERE JOB_NAME = '과장'
   AND SALARY > ALL (SELECT SALARY
                       FROM EMPLOYEE
                       JOIN JOB USING(JOB_CODE)
                      WHERE JOB_NAME = '차장');

-- 자기 직급의 평균 급여를 받고 있는 직원의
-- 사번, 이름, 직급, 급여를 조회하세요
-- 단, 급여와 급여 평균은 만원단위로 계산하세요 TRUNC(컬럼명, -5)
SELECT JOB_CODE, TRUNC(AVG(SALARY), -5)
  FROM EMPLOYEE
 GROUP BY JOB_CODE;

SELECT EMP_ID, EMP_NAME, JOB_CODE, SALARY
  FROM EMPLOYEE
 WHERE TRUNC(SALARY, -5) IN (SELECT TRUNC(AVG(SALARY), -5)
                               FROM EMPLOYEE
                              GROUP BY JOB_CODE);

-- 다중행 다중열 서브쿼리를 이용한 해결
SELECT EMP_NAME, JOB_CODE, SALARY
  FROM EMPLOYEE
 WHERE (JOB_CODE, TRUNC(SALARY, -5)) IN (SELECT JOB_CODE, TRUNC(AVG(SALARY), -5)
                                           FROM EMPLOYEE
                                          GROUP BY JOB_CODE);

-- 다중열 서브쿼리
-- 퇴사한 여직원과 같은 부서, 같은 직급에 해당하는 사원의
-- 이름, 직급, 부서, 입사일 조회
SELECT EMP_ID, EMP_NAME, DEPT_CODE, JOB_CODE
  FROM EMPLOYEE
 WHERE SUBSTR(EMP_NO, 8, 1) = 2
   AND ENT_YN = 'Y';

SELECT EMP_NAME, JOB_CODE, DEPT_CODE, HIRE_DATE
  FROM EMPLOYEE
 WHERE DEPT_CODE IN (SELECT DEPT_CODE
                       FROM EMPLOYEE
                      WHERE SUBSTR(EMP_NO, 8, 1) = 2
                        AND ENT_YN = 'Y'
                    )
   AND JOB_CODE IN (SELECT JOB_CODE
                      FROM EMPLOYEE
                     WHERE SUBSTR(EMP_NO, 8, 1) = 2
                       AND ENT_YN = 'Y')
   AND EMP_ID NOT IN (SELECT EMP_ID
                        FROM EMPLOYEE
                       WHERE SUBSTR(EMP_NO, 8, 1) = 2
                         AND ENT_YN = 'Y'
                     );

-- 다중열 서브쿼리로 변경
SELECT EMP_NAME, JOB_CODE, DEPT_CODE, HIRE_DATE
  FROM EMPLOYEE
 WHERE (DEPT_CODE, JOB_CODE) IN (SELECT DEPT_CODE, JOB_CODE
                                   FROM EMPLOYEE
                                  WHERE SUBSTR(EMP_NO, 8, 1) = 2
                                    AND ENT_YN = 'Y')
   AND EMP_ID NOT IN (SELECT EMP_ID
                        FROM EMPLOYEE
                       WHERE SUBSTR(EMP_NO, 8, 1) = 2
                         AND ENT_YN = 'Y');

-- 서브쿼리의 사용 위치
-- SELECT절, FROM절, WHERE절, HAVING절, GROUP BY절, ORDER BY절
-- DML 구문 : INSERT문, UPDATE문
-- DDL 구문 : CREATE TABLE문, CREATE VIEW문

-- FROM 절에서 서브쿼리를 이용할 수 있다 : 테이블 대신에 사용
-- 인라인 뷰(INLINE VIEW)라고 함
SELECT JOB_CODE, TRUNC(AVG(SALARY), -5) AS JOBAVG
  FROM EMPLOYEE
 GROUP BY JOB_CODE;

SELECT EMP_NAME, JOB_NAME, SALARY
  FROM (SELECT JOB_CODE, TRUNC(AVG(SALARY), -5) AS JOBAVG
          FROM EMPLOYEE
         GROUP BY JOB_CODE) V
  JOIN EMPLOYEE E ON (JOBAVG = SALARY AND E.JOB_CODE = V.JOB_CODE)
  JOIN JOB J ON(E.JOB_CODE = J.JOB_CODE)
 ORDER BY JOB_NAME;
 
SELECT EMP_NAME, DEPT_TITLE AS 부서명, JOB_NAME AS 직급이름
  FROM EMPLOYEE
  LEFT JOIN DEPARTMENT ON (DEPT_ID = DEPT_CODE)
  JOIN JOB USING(JOB_CODE);

SELECT 
       EMP_NAME
     , 부서명
     , 직급이름
  FROM (SELECT EMP_NAME
             , DEPT_TITLE AS 부서명
             , JOB_NAME AS 직급이름
          FROM EMPLOYEE
          LEFT JOIN DEPARTMENT ON (DEPT_ID = DEPT_CODE)
          JOIN JOB USING(JOB_CODE)
       )
 WHERE 부서명 = '인사관리부';

-- 인라인뷰를 활용한 TOP-N 분석
-- ROWNUM은 행 번호를 의미함
-- WHERE절을 수행 할 때 붙여진다.
SELECT ROWNUM, EMP_NAME, SALARY
FROM EMPLOYEE
ORDER BY SALARY;

SELECT * 
  FROM EMPLOYEE
 ORDER BY SALARY DESC;

SELECT ROWNUM, EMP_NAME, SALARY
  FROM (SELECT * 
          FROM EMPLOYEE
         ORDER BY SALARY DESC)
 WHERE ROWNUM <= 5;

-- 급여 평균 3위 안에 드는 부서의
-- 부서코드와 부서명, 평균 급여를 조회하세요
SELECT DEPT_CODE, DEPT_TITLE, 평균급여
  FROM (SELECT DEPT_CODE, DEPT_TITLE, AVG(SALARY) 평균급여
          FROM EMPLOYEE
          JOIN DEPARTMENT ON (DEPT_CODE = DEPT_ID)
         GROUP BY DEPT_CODE, DEPT_TITLE
         ORDER BY AVG(SALARY) DESC)
 WHERE ROWNUM <= 3;

-- 직원 정보에서 급여를 가장 많이 받는 순으로 이름, 급여, 순위 조회
-- RANK() 함수 : 동일한 순위 이후의 등수를 동일한 인원 수 만큼 건너뛰고 다음 순위 계산
-- DENSE_RANK() 함수 : 중복되는 순위 이후의 등수를 이후 등수로 처리

SELECT 
       EMP_NAME
     , SALARY
     , DENSE_RANK() OVER(ORDER BY SALARY DESC) 순위
  FROM EMPLOYEE;

SELECT
       EMP_NAME
     , SALARY
     , RANK() OVER(ORDER BY SALARY DESC) 순위
  FROM EMPLOYEE;

SELECT
       *
  FROM (SELECT EMP_NAME
             , SALARY
             , RANK() OVER(ORDER BY SALARY DESC) 순위
          FROM EMPLOYEE)
WHERE 순위 <= 5;

-- 직원 테이블에서 보너스 포함한 연봉이 높은 5명의
-- 사번, 이름, 부서명, 직급명, 입사일을 조회하세요
SELECT EMP_ID, EMP_NAME, DEPT_TITLE, JOB_NAME, HIRE_DATE, 순위
  FROM (SELECT EMP_ID, EMP_NAME, DEPT_TITLE, JOB_NAME, HIRE_DATE, SALARY,
               (SALARY + (SALARY * NVL(BONUS, 0))) * 12,
               RANK() OVER(ORDER BY (SALARY + (SALARY * NVL(BONUS, 0)) * 12) DESC) 순위
          FROM EMPLOYEE E
          JOIN DEPARTMENT D ON(E.DEPT_CODE = D.DEPT_ID)
          JOIN JOB J ON(E.JOB_CODE = J.JOB_CODE))
 WHERE 순위 < 6;

-- 상[호연]관 서브쿼리
-- 일반적으로는 서브쿼리가 만든 결과값을 메인쿼리가 비교 연산하지만
-- 메인쿼리가 사용하는 테이블의 값을 서브쿼리가 이용해서 결과를 만듦
-- 메인쿼리의 테이블 값이 변경되면, 서브쿼리의 결과값도 바뀌게 됨

-- 관리자 사번이 EMPLOYEE 테이블에 존재하는 직원에 대해 조회
SELECT EMP_ID, EMP_NAME, DEPT_CODE, MANAGER_ID
  FROM EMPLOYEE E
 WHERE EXISTS (SELECT EMP_ID
                 FROM EMPLOYEE M
                WHERE E.MANAGER_ID = M.EMP_ID);

-- 동일 직급의 급여 평균보다 급여를 많이 받고 있는 직원의
-- 사번, 직급코드, 급여를 조회하세요
SELECT EMP_NAME, JOB_CODE, SALARY
  FROM EMPLOYEE E
 WHERE SALARY > (SELECT TRUNC(AVG(SALARY), -5)
                   FROM EMPLOYEE M
                  WHERE E.JOB_CODE = M.JOB_CODE);

-- 스칼라 서브쿼리
-- 단일행 서브쿼리 + 상관쿼리
-- SELECT 절, WHERE절, ORDER BY절 사용 가능

-- SELECT 절에서 스칼라 서브쿼리 이용
-- 모든 사원의 사번, 이름, 관리자사번, 관리자명 조회
SELECT EMP_ID, EMP_NAME, MANAGER_ID,
       NVL((SELECT EMP_NAME
              FROM EMPLOYEE M
             WHERE E.MANAGER_ID = M.EMP_ID), '없음')
  FROM EMPLOYEE E
 ORDER BY 1;

-- ORDER BY 절에서 스칼라 서브쿼리 이용
-- 모든 직원의 사번, 이름, 소속부서 조회
-- 단, 부서명 내림차순 정렬
SELECT EMP_ID, EMP_NAME, DEPT_CODE
  FROM EMPLOYEE
 ORDER BY (SELECT DEPT_TITLE
             FROM DEPARTMENT
            WHERE DEPT_CODE = DEPT_ID) DESC NULLS LAST;
반응형

'Programming > DB' 카테고리의 다른 글

[Oracle] PRIMARY KEY(기본키) 제약조건  (0) 2022.03.07
[Oracle] 테이블 생성 및 데이터 insert  (0) 2022.03.07
[Oracle] JOIN  (0) 2022.03.07
[Oracle] GROUP BY & HAVING  (0) 2022.03.07
[Oracle] 함수  (0) 2022.03.07

댓글