1) 개념
1) 단일행 서브쿼리
-- 노옹철 사원의 급여보다 많이 받는 직원의 사번, 이름, 부서, 직급, 급여 조회
SELECT
EMP_ID
, EMP_NAME
, DEPT_CODE
, JOB_CODE
, SALARY
FROM EMPLOYEE
WHERE SALARY > (SELECT
SALARY
FROM EMPLOYEE
WHERE EMP_NAME = '노옹철'
);
-- 부서별 급여의 합계 중 합계가 가장 큰 부서의 부서명, 급여 합계를 조회
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
);
2) 다중행 서브쿼리
-- 부서별 최고 급여를 받는 직원의 이름, 직급, 부서, 급여 조회
SELECT
EMP_NAME
, JOB_CODE
, DEPT_CODE
, SALARY
FROM EMPLOYEE
WHERE SALARY IN (SELECT
MAX(SALARY)
FROM EMPLOYEE
GROUP BY DEPT_CODE
);
-- 대리 직급의 직원들 중에서
-- 과장 직급의 최소 급여보다 많이 받는 직원의 사번, 이름, 직급명, 급여 조회
SELECT
EMP_ID
, EMP_NAME
, JOB_NAME
, SALARY
FROM EMPLOYEE
JOIN JOB USING(JOB_CODE)
WHERE JOB_NAME = '대리'
AND SALARY > ANY(SELECT
SALARY
FROM EMPLOYEE
JOIN JOB USING(JOB_CODE)
WHERE JOB_NAME = '과장'
);
-- 차장 직급의 급여의 가장 큰 값보다 많이 받는 과장 직급의
-- 사번, 이름, 직급명, 급여를 조회
SELECT
EMP_ID
, EMP_NAME
, JOB_CODE
, 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 = '차장'
);
3) 다중열 서브쿼리
-- 퇴직한 여사원과 같은 부서, 같은 직급에 해당하는 사원의 이름, 직급, 부서, 입사일 조회
SELECT
EMP_NAME
, JOB_CODE
, DEPT_CODE
, HIRE_DATE
FROM EMPLOYEE
WHERE DEPT_CODE = (SELECT
DEPT_CODE
FROM EMPLOYEE
WHERE SUBSTR(EMP_NO,8,1) = '2'
AND ENT_YN = 'Y'
)
AND JOB_CODE = (SELECT
DEPT_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, JOB_CODE) = (SELECT DEPT_CODE , JOB_CODE FROM EMPLOYEE WHERE SUBSTR(EMP_NO, 8,1) = '2' AND ENT_YN = 'Y' );
3-1) INLINE VIEW(인라인뷰)
-- 인라인뷰로 직급별 평균 급여를 계산한 테이블을 만들고,
-- EMPLOYEE와 JOIN시
-- 평균 급여가 본인의 급여와 동일하면 조인하게 조건을 줘서
-- 직급별 평균 급여에 맞는 급여를 받고 있는 직원을 조회하는 구문
SELECT
E.EMP_NAME
, J.JOB_NAME
, E.SALARY
FROM (SELECT
JOB_CODE
, TRUNC(AVG(SALARY),-5) AS JOBAVG
FROM EMPLOYEE
GROUP BY JOB_CODE) V
JOIN EMPLOYEE E ON(V.JOBAVG = E.SALARY AND V.JOB_CODE = E.JOB_CODE)
JOIN JOB J ON (V.JOB_CODE = J.JOB_CODE)
ORDER BY J.JOB_NAME;
주의할 점
인라인뷰를 사용한 TOP-N 분석
-- 현재는 WHERE절에서 ROWNUM이 결정되어 급여를 많이 받는 순서와 관계 없는 번호를 가짐
SELECT
ROWNUM
, EMP_NAME
, SALARY
FROM EMPLOYEE
ORDER BY SALARY DESC;
-- 따라서 원하는 순서의 ROWNUM이 붙게 하려면 인라인뷰를 활용해야 한다.
SELECT
ROWNUM
, V.EMP_NAME
, V.SALARY
FROM (SELECT E.*
FROM EMPLOYEE E
ORDER BY E.SALARY DESC
) V
WHERE ROWNUM <= 5;
SELECT
ROWNUM
, V.EMP_NAME
, V.SALARY
FROM (SELECT E.*
FROM EMPLOYEE E
ORDER BY E.SALARY DESC
) V
WHERE ROWNUM BETWEEN 6 AND 10;
-- FROM 절의 ROWNUM에 별칭을 지어 컬럼이 생성된 것처럼 함.
SELECT
V2.RNUM
, V2.EMP_NAME
, V2.SALARY
FROM (SELECT
ROWNUM RNUM
, V.EMP_NAME
, V.SALARY
FROM (SELECT E.*
FROM EMPLOYEE E
ORDER BY E.SALARY DESC
) V
) V2
WHERE RNUM BETWEEN 6 AND 10;
SELECT
V2.RNUM
, V2.EMP_NAME
, V2.SALARY
FROM (SELECT
ROWNUM RNUM
, V.EMP_NAME
, V.SALARY
FROM (SELECT E.*
FROM EMPLOYEE E
ORDER BY E.SALARY DESC
) V
WHERE ROWNUM < 11
) V2
WHERE RNUM BETWEEN 6 AND 10;
RANK()
SELECT
EMP_NAME
, SALARY
, RANK() OVER(ORDER BY SALARY DESC) 순위
FROM EMPLOYEE;
DENSE_RANK()
WITH 이름 AS (쿼리문)
WITH
TOPN_SAL
AS (SELECT
E.EMP_ID
, E.EMP_NAME
, E.SALARY
FROM EMPLOYEE E
ORDER BY E.SALARY DESC
)
SELECT
ROWNUM
, T.EMP_NAME
, T.SALARY
FROM TOPN_SAL T;
4) 상호연관 서브쿼리
-- 관리자 사번이 EMPLOYEE 테이블에 존재하는 직원에 대한 조회
-- 메인쿼리로부터 MANAGER_ID 를 가져오고 그것을 EMP_ID와 비교해서 조회.
-- MANAGER_ID가 NULL인 경우 그것의 존재 여부가(EXISTS) FALSE이므로 아무것도 조회 안됨
-- 따라서 MANAGER_ID가 NULL이 아닌 경우에 조회 가능
-- 서브쿼리에서 조회한 값이 있으면 메인 쿼리 SELECT 수행
-- MANAGER_ID와 EMP_ID가 같은 값이 있느냐를 따져서 그것만 SELECT
SELECT
E.EMP_ID
, E.EMP_NAME
, E.DEPT_CODE
, E.MANAGER_ID
FROM EMPLOYEE E
WHERE EXISTS(SELECT
E2.EMP_ID
FROM EMPLOYEE E2
WHERE E.MANAGER_ID = E2.EMP_ID
);
5) 스칼라 서브쿼리
-- 동일 직급의 급여 평균보다 급여를 많이 받고 있는 직원의 직원명, 직급코드, 급여 조회
SELECT
EMP_NAME
, JOB_CODE
, SALARY
FROM EMPLOYEE E
WHERE SALARY > (SELECT
TRUNC(AVG(E2.SALARY), -5)
FROM EMPLOYEE E2
WHERE E.JOB_CODE = E2.JOB_CODE
);
-- 모든 사원의 사번, 이름, 관리자 사번, 관리자명을 조회
-- SELECT 절에서 스칼라 서브쿼리 사용
SELECT
EMP_ID
, EMP_NAME
, MANAGER_ID
, NVL((SELECT EMP_NAME
FROM EMPLOYEE E2
WHERE E.MANAGER_ID = E2.EMP_ID
), '없음') 관리자명
FROM EMPLOYEE E
ORDER BY 1;