
쿼리 안에 또다른 쿼리(SELECT)가 있는 것입니다. 메인 SQL문을 위해 보조 역할을 하는 쿼리를 의미합니다.
간단한 서브 쿼리 예시를 살펴보겠습니다.
노옹철 사원과 같은 부서에 속한 사원들을 조회하는 쿼리를 작성
위 예시는 두 가지로 구성되어 있는 것을 알 수 있습니다.
각각을 쿼리로 작성하면 다음과 같습니다.
SELECT DEPT_CODE
FROM EMPLOYEE
WHERE EMP_NAME = '노옹철';
SELECT EMP_NAME
FROM EMPLOYEE
WHERE DEPT_CODE = 'D1';
이 둘을 합치려면 서브쿼리를 사용해야됩니다. 이는 아래와 같습니다.
SELECT EMP_NAME
FROM EMPLOYEE
WHERE DEPT_CODE = (SELECT DEPT_CODE
FROM EMPLOYEE
WHERE EMP_NAME = '노옹철');
즉 'D1' 이 1번 쿼리로 대치된 것을 알 수 있습니다. 예시 하나를 더 살펴보도록 하겠습니다.
전 직원의 평균급여보다 더 많은 급여를 받는 사원들의 사번, 이름, 직급코드, 급여 조회
SELECT ROUND(AVG(SALARY))
FROM EMPLOYEE;
SELECT EMP_ID, EMP_NAME, JOB_CODE, SALARY
FROM EMPLOYEE
WHERE SALARY >= 3000000;
서브쿼리를 통한 통합
SELECT EMP_ID, EMP_NAME, JOB_CODE, SALARY
FROM EMPLOYEE
WHERE SALARY >= (SELECT AVG(SALARY)
FROM EMPLOYEE);
서브쿼리를 수행한 결과값이 몇행 몇열로 나오냐에 따라서 분류합니다.
크게 정리하면 아래와 같습니다.
단일행 서브쿼리 : 서브쿼리의 조회 결과값이 갯수가 오로지 1개일 때
다중행 서브쿼리 : 서브쿼리의 조회 결과값이 여러행일 때(여러행 한열)
다중열 서브쿼리 : 서브쿼리의 조회 결과값이 한 행이지만 컬럼이 여러개일 때
다중행 다중열 서브쿼리 : 서브쿼리의 조회 결과값이 여러행 여러열일 때
이 때, 서브쿼리의 종류가 뭐냐에 따라서 서브쿼리 앞에 붙는 연산자 달라집니다.
= != > <= ...) 사용 가능예를 들어 노옹철 사원의 급여보다 많이받는 사원들의 사번, 이름, 부서코드, 급여조회를 할 경우 아래와 같습니다.
SELECT EMP_ID, EMP_NAME, DEPT_CODE, SALARY
FROM EMPLOYEE
WHERE SALARY > (SELECT SALARY
FROM EMPLOYEE
WHERE EMP_NAME = '노옹철');
이 때 서브쿼리의 결과 노옹철 사원의 급여에 해당하는 단 1행 1열의 결과를 사용하기 때문에 이를 단일행 서브쿼리라 합니다.
'IN (서브쿼리)' : 여러개의 결과값 중에서 한개라도 일치하는 값이 있다면 조회
'> ANY (서브쿼리)' : 여러개의 결과값 중에서 한개라도 클 경우
'< ANY (서브쿼리)' : 여러개의 결과값 중에서 한개라도 작을 경우 조회
비교대상 > ANY (서브쿼리의 결과값 => 값1, 값2, 값3...)
비교대상 > 값1 OR 비교대상 > 값2 OR 비교대상 > 값3
'> ALL (서브쿼리)' : 여러개의 모든 결과값들 보다 클 경우 조회
'< ALL (서브쿼리)' : 여러개의 모든 결과값들 보다 작을 경우
비교대상 > ALL (서브쿼리의 결과값 => 값1, 값2, 값3...)
비교대상 > 값1 AND 비교대상 > 값2 AND 비교대상 > 값3...
SELECT EMP_ID, EMP_NAME, JOB_CODE, SALARY
FROM EMPLOYEE
WHERE JOB_CODE IN (SELECT JOB_CODE
FROM EMPLOYEE
WHERE EMP_NAME IN ('윤은해', '유재식'));
서브쿼리의 결과 행은 2개(유재식, 윤은해의 JOB_CODE) 열은 1개(JOB_CODE 열) 이므로 다중행 서브쿼리입니다.
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
JOIN JOB USING (JOB_CODE)
WHERE JOB_NAME = '과장');
WHERE (열1, 열2, ...) = (조건)
SELECT EMP_NAME, DEPT_CODE, JOB_CODE, HIRE_DATE
FROM EMPLOYEE
WHERE (DEPT_CODE, JOB_CODE) = (SELECT DEPT_CODE, JOB_CODE
FROM EMPLOYEE
WHERE EMP_NAME = '하이유');
SELECT EMP_ID, EMP_NAME, DEPT_CODE, SALARY
FROM EMPLOYEE
WHERE (DEPT_CODE, SALARY) IN (SELECT DEPT_CODE, MAX(SALARY)
FROM EMPLOYEE
GROUP BY DEPT_CODE);
SELECT EMP_NAME, SALARY, HIRE_DATE
FROM (SELECT EMP_NAME, SALARY, HIRE_DATE
FROM EMPLOYEE
ORDER BY HIRE_DATE DESC)
WHERE ROWNUM <= 5;
순위를 매기는 방법
ROWNUM: 조회된 순서대로 1부터 순번을 부여해주는 컬럼입니다.
RANK() OVER(정렬기준): 공동 순위가 있으면 건너뛰고 순위 계산합니다.
DENSE_RANK() OVER(정렬기준): 공동 순위 상관없이 순위 계산합니다.
(중요)서브쿼리 내에서 함수를 사용할 경우 메인쿼리에서는 이를 컬럼명으로 제대로 인식하지 못합니다. 반드시 별칭을 지정하도록 합니다.
SELECT *
FROM (SELECT EMP_NAME, SALARY, RANK() OVER(ORDER BY SALARY DESC)
FROM EMPLOYEE)
WHERE RANK() OVER(ORDER BY SALARY DESC) <= 5; -- 오류 발생
SELECT *
FROM (SELECT EMP_NAME, SALARY, RANK() OVER(ORDER BY SALARY DESC) AS "순위"
FROM EMPLOYEE)
WHERE 순위 <= 5;