전체 방문자
오늘
어제
21종
종이의 코딩 공부방
21종
  • 분류 전체보기 (171)
    • JAVA (64)
    • Springboot (46)
      • 블로그만들기 (45)
    • Database (60)
      • Oracle (60)
    • 프로젝트 3 (CELOVER) (0)
    • 개발서버 구축 (0)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

인기 글

최근 글

최근 댓글

hELLO · Designed By 정상우.
21종

종이의 코딩 공부방

Database/Oracle

[Oracle] 서브쿼리 (Subquery)

2023. 7. 13. 17:31

< 서브쿼리 (SUBQUERY) >

- 하나의 SQL문 안에 포함된 또 다른 SELECT문
- 메인 SQL문을 위해 보조 역할을 하는 쿼리문

 

* 서브쿼리의 구분

   서브쿼리 수행한 결과값이 몇 행 몇열이냐에 따라서 분류됨

  - 단일행 서브쿼리 : 서브쿼리의 조회 결과값의 개수가 오로지 1개 일 때 (한 행 한 열)
  - 다중행 서브쿼리 : 서브쿼리의 조회 결과값이 여러행 일 때(여러행 한열) => 동명이인 노옹철
  - 다중열 서브쿼리 : 서브쿼리의 조회 결과값이 한 행이지만 컬럼이 여러개일 때 (한 행 여러 열)
  - 다중행 다중열 서브쿼리 : 서브쿼리 조회 결과값이 여러 행 여러 컬럼일 때 (여러 행 여러 열)
    
    >> 서브쿼리 종류가 뭐냐에 따라서 서브쿼리 앞에 붙는 연산자가 달라짐!


 

-- 간단 서브쿼리 예시1
-- 노옹철 사원과 같은 부서에 속한 사원들 조회하고 싶음!!

-- 1) 먼저 노옹철 사원의 부서코드 조회
SELECT DEPT_CODE
FROM EMPLOYEE
WHERE EMP_NAME = '노옹철';    --D9

-- 2) 부서코드가 D9인 사원들 조회
SELECT EMP_NAME
FROM EMPLOYEE
WHERE DEPT_CODE = 'D9';

--> 위의 2단계를 하나의 쿼리문
SELECT EMP_NAME
FROM EMPLOYEE
WHERE DEPT_CODE = (SELECT DEPT_CODE
                    FROM EMPLOYEE
                    WHERE EMP_NAME = '노옹철');

 

 

-- 간단 서브쿼리 예시2
-- 전 직원의 평균급여보다 더 많은 급여를 받는 사원들의 사번, 이름, 직급코드, 급여 조회

-- 1) 전 직원의 평균 급여 조회
SELECT AVG(SALARY)
FROM EMPLOYEE;  -- 대략 3047663원 인걸 알아냄!!

-- 2) 급여가 3047663원 이상인 사원들 조회
SELECT EMP_ID, EMP_NAME, JOB_CODE, SALARY
FROM EMPLOYEE
WHERE SALARY >= 3047663;

-- 위의 2단계를 하나의 쿼리문으로!!
SELECT EMP_ID, EMP_NAME, JOB_CODE, SALARY
FROM EMPLOYEE
WHERE SALARY >= (SELECT AVG(SALARY)
                FROM EMPLOYEE);

 

 


1. 단일행 서브쿼리 (SINGKE ROW SUBQUERY)

서브쿼리의 조회 결과값의 개수가 오로지 1개일 때 (한행 한열)
일반 비교 연산자 사용 가능
 =, !=, ^=, <>, <, >, >= ...

-- 1) 전 직원의 평균급여보다 급여를 더 적게 받는 사원들의 사원명, 직급코드, 급여조회
SELECT EMP_NAME, JOB_CODE, SALARY
FROM EMPLOYEE
WHERE SALARY < (SELECT AVG(SALARY)
                FROM EMPLOYEE);

 

-- 2) 최저 급여를 받는 사원의 사번, 이름, 급여, 입사일
SELECT EMP_ID, EMP_NAME, SALARY, HIRE_DATE
FROM EMPLOYEE
--WHERE SALARY = 전직원들중최저급여;
WHERE SALARY = (SELECT MIN(SALARY)
                FROM EMPLOYEE);

 

-- 3) 노옹철 사원의 급여보다 더 많이 받는 사원들의 사번, 이름, 부서코드, 급여 조회
SELECT EMP_ID, EMP_NAME,DEPT_CODE, SALARY
FROM EMPLOYEE
--WHERE SALARY > 노옹철사원의급여
WHERE SALARY > (SELECT SALARY
                FROM EMPLOYEE
                WHERE EMP_NAME = '노옹철');
    
-->> 오라클 전용구문
SELECT EMP_ID, EMP_NAME, DEPT_CODE, SALARY, DEPT_TITLE
FROM EMPLOYEE, DEPARTMENT
WHERE DEPT_CODE = DEPT_ID
AND SALARY > (SELECT SALARY
                FROM EMPLOYEE
                WHERE EMP_NAME = '노옹철');

-->> ANSI구문
SELECT EMP_ID, EMP_NAME, DEPT_CODE, SALARY, DEPT_TITLE
FROM EMPLOYEE
JOIN DEPARTMENT ON (DEPT_CODE = DEPT_ID)
WHERE SALARY > (SELECT SALARY
                FROM EMPLOYEE
                WHERE EMP_NAME = '노옹철');

 

 

-- 4) 부서별 급여합이 가장 큰 부서의 부서코드, 급여 합 조회

    -- 4_1) 먼저 부서별 급여합 중에서도 가장 큰 값 하나만 조회
    SELECT MAX(SUM(SALARY))
    FROM EMPLOYEE
    GROUP BY DEPT_CODE; --17700000

    -- 4_2) 부서별 급여합이 17700000원인 부서 조회 (부서코드, 급여합)
    SELECT DEPT_CODE, SUM(SALARY)
    FROM EMPLOYEE
    GROUP BY DEPT_CODE
    HAVING SUM(SALARY) = 17700000;

    -- 위의 두 단계를 하나의 쿼리문으로!
    SELECT DEPT_CODE, SUM(SALARY)
    FROM EMPLOYEE
    GROUP BY DEPT_CODE
    HAVING SUM(SALARY) = (SELECT MAX(SUM(SALARY))
                            FROM EMPLOYEE
                            GROUP BY DEPT_CODE);

 

-- 직접해보기
-- 전지연 사원과 같은 부서원들의 사번, 사원명, 전환번호, 입사일, 부서명
-- 단, 전지연은 제외!
-- 먼저 테이블에 어떤 컬럼이 있는지 조회해보자!
SELECT * FROM EMPLOYEE;
SELECT * FROM DEPARTMENT;

-->> 오라클 전용 구문
SELECT EMP_ID, EMP_NAME, PHONE, HIRE_DATE, DEPT_TITLE
FROM EMPLOYEE, DEPARTMENT
WHERE DEPT_CODE = DEPT_ID
AND DEPT_CODE = (SELECT DEPT_CODE
                    FROM EMPLOYEE
                    WHERE EMP_NAME = '전지연')
AND EMP_NAME != '전지연';

-->> ANSI 구문
SELECT EMP_ID, EMP_NAME, PHONE, HIRE_DATE, DEPT_TITLE
FROM EMPLOYEE
JOIN DEPARTMENT ON(DEPT_CODE = DEPT_ID)
WHERE DEPT_CODE = (SELECT DEPT_CODE
                    FROM EMPLOYEE
                    WHERE EMP_NAME = '전지연')
AND EMP_NAME != '전지연';

2. 다중행 서브쿼리(MULTI ROW SUBQUERY)

서브쿼리를 수행한 결과값이 여러행 일때 (컬럼(열)은 한개)
    
- IN 서브쿼리 : 여러개의 결과값 중에서 한개라도 일치하는 값이 있다면 
    
- > ANY 서브쿼리 : 여러개의 결과값 중에서 "한개라도" 클 경우 (여러개의 결과값 중에서 가장 작은 값보다 클 경우)
- < ANY 서브쿼리 : 여러개의 결과값 중에서 "한개라도" 작을 경우 (여러개의 결과값 중에서 가장 큰 값 보다 작은 경우)
    
비교대상 > ANY (값1, 값2, 값3)
비교대상 > 값1 OR 비교대상 > 값2 OR 비교대상 > 값3
    
-> ALL 서브쿼리 : 여러개의 "모든" 결과값들 보다 클 경우
-< ALL 서브쿼리 : 여러개의 "모든" 결과값들 보다 작을 경우
    
비교대상 > ALL (값1, 값2, 값3)
비교대상 > 값1 AND 비교대상 > 값2 AND 비교대상 > 값3

 

-- 1) 유재식 또는 윤은해 사원과 같은 직급인 사원들의 사번, 사원명, 직급코드, 급여
    -- 1_1) 유재식 또는 윤은해 사원이 어떤 직급인지 조회
    SELECT JOB_CODE
    FROM EMPLOYEE
    WHERE EMP_NAME IN ('유재식', '윤은해');   -- J3, J7

    -- 1_2) J3, J7인 직급의 사원들 조회
    SELECT EMP_ID, EMP_NAME, JOB_CODE, SALARY
    FROM EMPLOYEE
    WHERE JOB_CODE IN('J3','J7');

    -- 위의 두 단계를 하나의 쿼리로
    SELECT EMP_ID, EMP_NAME, JOB_CODE, SALARY
    FROM EMPLOYEE
    WHERE JOB_CODE IN(SELECT JOB_CODE
                        FROM EMPLOYEE
                        WHERE EMP_NAME IN ('유재식', '윤은해'));   -- = 이라고 쓰면 에러남 !! 여러행으로 조회됐기 때문
                        -- 만약에 결과값이 여러개 나올 것 같으면 그냥 안전빵으로 IN으로 가자

 

-- 2) 대리 직급임에도 불구하고 과장 직급 급여들 중 최소 급여보다 많이 받는 직원 조회 (사번, 이름, 직급, 급여)

-- 사원 => 대리 => 과장 => 차장 => 부장....

-- 2_1) 먼저 과장 직급인 사원들의 급여 조회
SELECT SALARY
FROM EMPLOYEE E, JOB J
WHERE E.JOB_CODE = J.JOB_CODE
AND J.JOB_NAME = '과장';  -- 2200000, 2500000, 3760000

-- 2_2) 직급이 대리이면서 급여값이 위의 목록들 값 중에 하나라도 큰 사원
SELECT EMP_ID, EMP_NAME, JOB_NAME, SALARY
FROM EMPLOYEE
JOIN JOB USING(JOB_CODE)
WHERE JOB_NAME = '대리'
--AND SALARY > IN (2200000, 2500000, 3760000); -- 안된다.
AND SALARY > ANY (2200000, 2500000, 3760000);

-- 위의 두 단계를 하나의 쿼리문으로 작성
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 E, JOB J
                    WHERE E.JOB_CODE = J.JOB_CODE
                    AND J.JOB_NAME = '과장');                    

-- 위의 쿼리를 단일행 서브쿼리로도 가능!!
SELECT EMP_ID, EMP_NAME, JOB_NAME, SALARY
FROM EMPLOYEE
JOIN JOB USING(JOB_CODE)
WHERE JOB_NAME = '대리'
AND SALARY > (SELECT MIN(SALARY)
                FROM EMPLOYEE E, JOB J
                WHERE E.JOB_CODE = J.JOB_CODE
                AND J.JOB_NAME = '과장');

 

-- 3) 과장 직급임에도 불구하고 차장직급인 사원들의 모든 급여보다도 더 많이 받는 사원들의 사번, 이름, 직급명, 급여 조회
SELECT EMP_ID, EMP_NAME, JOB_NAME, SALARY
FROM EMPLOYEE
JOIN JOB USING(JOB_CODE)
WHERE JOB_NAME = '과장'
--AND SALARY > (차장 직급인 애들의 급여들)
AND SALARY > ALL (SELECT SALARY
                FROM EMPLOYEE
                JOIN JOB USING(JOB_CODE)
                WHERE JOB_NAME = '차장');

3. 다중열 서브쿼리

결과값은 한 행이지만 나열된 컬럼수가 여러개일 경우

 

-- 1) 하이유 사원과 같은 부서코드, 같은 직급코드에 해당하는 사원들 조회(사원명 부서코드, 직급코드, 입사일자)

-- 단일행 서브쿼리 ** 2개의 서브쿼리로 작성할것
SELECT EMP_NAME, DEPT_CODE, JOB_CODE, HIRE_DATE
FROM EMPLOYEE
-- WHERE DEPT_CODE = 하이유사원의부서코드
WHERE DEPT_CODE = 'D5'
-- AND JOB_CODE = 하이유사원의직급코드
AND JOB_CODE = 'J5';

SELECT DEPT_CODE --'D5'
FROM EMPLOYEE
JOIN DEPARTMENT ON (DEPT_CODE = DEPT_ID)
WHERE EMP_NAME = '하이유';

SELECT JOB_CODE -- 'J5'
FROM EMPLOYEE
JOIN JOB USING (JOB_CODE)
WHERE EMP_NAME = '하이유';                    

-- 위 코드를 합쳐보자 !

SELECT EMP_NAME, DEPT_CODE, JOB_CODE, HIRE_DATE
FROM EMPLOYEE
WHERE DEPT_CODE = (SELECT DEPT_CODE
                    FROM EMPLOYEE
                    JOIN DEPARTMENT ON (DEPT_CODE = DEPT_ID)
                    WHERE EMP_NAME = '하이유')
AND JOB_CODE = (SELECT JOB_CODE
                FROM EMPLOYEE
                JOIN JOB USING (JOB_CODE)
                WHERE EMP_NAME = '하이유');            

-->> 다중열 서브쿼리로!
SELECT EMP_NAME, DEPT_CODE, JOB_CODE, HIRE_DATE
FROM EMPLOYEE
--WHERE (DEPT_CODE, JOB_CODE) = (하이유사원의 부서코드, 하이유사원의 직급코드);
WHERE (DEPT_CODE, JOB_CODE) = (SELECT DEPT_CODE, JOB_CODE 
                                FROM EMPLOYEE
                                WHERE EMP_NAME = '하이유'); -- 순서중요함!! 개수 맞춰야함!!

 

-- 박나라 사원과 같은 직급코드, 같은 사수를 가지고 있는 사원들의 사번, 사원명, 직급코드, 사수사번 조회
SELECT EMP_ID, EMP_NAME, JOB_CODE, MANAGER_ID
FROM EMPLOYEE
WHERE (JOB_CODE, MANAGER_ID) = (SELECT JOB_CODE, MANAGER_ID
                                FROM EMPLOYEE
                                WHERE EMP_NAME = '박나라');

4. 다중행 다중열 서브쿼리

서브쿼리 조회 결과값 여러행 여러열 인 경우

-- 1) 각 직급별 최소급여를 받는 사원 조회 (사번, 사원명, 직급코드, 급여)
-- >> 각 직급별 최소급여 조회
SELECT JOB_CODE, MIN(SALARY)    -- 3.
FROM EMPLOYEE   -- 1.
GROUP BY JOB_CODE;   -- 2.

SELECT EMP_ID, EMP_NAME, JOB_CODE, SALARY
FROM EMPLOYEE
WHERE JOB_CODE = 'J2' AND SALARY = 3700000
OR    JOB_CODE = 'J7' AND SALARY = 1380000
;

-- 서브쿼리를 적용해서 해보자!!
SELECT EMP_ID, EMP_NAME, JOB_CODE, SALARY
FROM EMPLOYEE
WHERE (JOB_CODE, SALARY) IN (SELECT JOB_CODE, MIN(SALARY)
                            FROM EMPLOYEE   
                            GROUP BY JOB_CODE);

 

-- 2) 각 부서별 최고 급여를 받는 사원들의 사번, 사원명, 부서코드, 급여
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);

5. 인라인 뷰 (INLINE - VIEW)

서브쿼리를 수행한 결과를 마치 테이블 처럼 사용!

-- 사원들의 사번, 이름, 보너스 포함연봉(별칭부여 : 연봉), 부서코드 조회 => 보너스 포함 연봉이 절대 NULL이 안나오게
-- 단, 보너스 포함 연봉이 3000만원 이상인 사원들만 조회
SELECT EMP_NO, EMP_NAME, (SALARY + SALARY * NVL(BONUS,0)) * 12 AS "연봉", DEPT_CODE -- 3.
FROM EMPLOYEE -- 1.
WHERE (SALARY + SALARY * NVL(BONUS,0)) * 12 >= 30000000; --2. 연봉 이라고 못쓴다.

SELECT EMP_NO, EMP_NAME, (SALARY + SALARY * NVL(BONUS,0)) * 12 AS "연봉", DEPT_CODE -- 3.
FROM EMPLOYEE; -- 1.
-- 이걸 마치 존재하는 테이블인거 마냥 사용할 수 있음!! 그게 인라인 뷰

SELECT *
FROM (SELECT EMP_NO, EMP_NAME, (SALARY + SALARY * NVL(BONUS,0)) * 12 AS "연봉", DEPT_CODE 
FROM EMPLOYEE) --1
WHERE 연봉 >= 30000000;

SELECT EMP_NO, EMP_NAME, 연봉, DEPT_CODE --, MANAGER_ID 이건 오류남!!
FROM (SELECT EMP_NO, EMP_NAME, (SALARY + SALARY * NVL(BONUS,0)) * 12 AS "연봉", DEPT_CODE 
FROM EMPLOYEE) --1
WHERE 연봉 >= 30000000;

 

-- >> 인라인 뷰를 주로 사용하는 예 => TOP_N 분석 (상위 몇개만 보여주고 싶을 때 => BEST 상품!)

 

-- 전 직원 중 급여가 가장 높은 상위 5명만 조회 -- * ROWNUM : 오라클 제공해주는 컬럼, 조회된 순서대로 1부터 순번을 부여해주는 컬럼
SELECT ROWNUM, EMP_NAME, SALARY -- 2.
FROM EMPLOYEE -- 1.
ORDER BY SALARY DESC; -- 3.
-- FROM -> SELECT ROWNUM ( 이때 순번이 부여됨. 정렬도 하기전에 이미 순번 부여)

 

-- 뭔가 좀 이상함... 실행 순서 때문
-- 1. FROM 실행
-- 2. SELECT 실행 (ROWNUM 부여)
-- 3. ORDER BY 정렬 (ROWNUM 부여된 후 정렬되기 때문에 순번이 섞인다)

 

SELECT ROWNUM, EMP_NAME, SALARY -- 3.
FROM EMPLOYEE -- 1.
WHERE ROWNUM <= 5 -- 2.
ORDER BY SALARY DESC; -- 4.
--> 정상적인 결과가 조회되지 않음!!(정렬이 되기도 전에 5명이 추려지고 나서 정렬)

-- ORDER BY 절이 다 수행된 결과를 가지고 ROWNUM 부여 후 5명 추려야함!!
SELECT EMP_NAME, SALARY, DEPT_CODE -- 2.
FROM EMPLOYEE -- 1.
ORDER BY SALARY DESC; -- 3.

SELECT EMP_NAME, SALARY -- 3.
FROM (SELECT *--EMP_NAME, SALARY, DEPT_CODE 
        FROM EMPLOYEE
        ORDER BY SALARY DESC) -- 1.
WHERE ROWNUM <= 5; -- 2.

-- ROWNUM 이랑 전체컬럼 조회하고 싶음 => 별칭 부여 하는 방법으로
SELECT ROWNUM, E.* --EMP_NAME, SALARY 
FROM (SELECT * --EMP_NAME, SALARY, DEPT_CODE 
        FROM EMPLOYEE
        ORDER BY SALARY DESC) E 
WHERE ROWNUM <= 5;

 

 

-- 가장 최근에 입사한 사원 5명 조회(사원명, 급여, 입사일)
SELECT EMP_NAME, SALARY, HIRE_DATE
FROM (SELECT *
        FROM EMPLOYEE
        ORDER BY HIRE_DATE DESC)
WHERE ROWNUM <= 5;

 

 

-- 2. 각 부서별 평균급여가 높은 3개의 부서 조회 (부서코드, 평균급여)
SELECT *
FROM (SELECT DEPT_CODE, FLOOR(AVG(SALARY))
        FROM EMPLOYEE 
        GROUP BY DEPT_CODE 
        ORDER BY 2 DESC)
WHERE ROWNUM <= 3;

SELECT DEPT_CODE, AVG(SALARY) AS "평균급여"
FROM EMPLOYEE 
GROUP BY DEPT_CODE 
ORDER BY 2 DESC;

--SELECT DEPT_CODE, AVG(SALARY) --> 얘는 안된다, AVG(SALARY)를 함수로 인식하기 때문에 별칭을 꼭 줘야된다.
SELECT DEPT_CODE, FLOOR(평균급여)
FROM (SELECT DEPT_CODE, AVG(SALARY) AS "평균급여"
        FROM EMPLOYEE 
        GROUP BY DEPT_CODE 
        ORDER BY 2 DESC)
WHERE ROWNUM <= 3;

 

    'Database/Oracle' 카테고리의 다른 글
    • [Oracle] JOIN 및 subquery 실습문제
    • [Oracle] 순위를 매기는 함수 (Window Function)
    • [Oracle] 종합(~JOIN)_실습문제
    • [Oracle] 함수(FUNCTION)_실습문제
    21종
    21종
    코딩 공부한 것 정리하려고 만든 블로그

    티스토리툴바