안녕하세요 오늘은 SELECT문의 마지막 관문이라고 할 수 있는
SUBQUERY에 대해 정리해보았습니다!!😊😊✌️
SELECT문에 또다른 SELECT문을 작성하는 것이다.
- 주 SELECT 문 보조 SELECT 문
- 서브쿼리문은 반드시 괄호로 묶어줘야하한다
- 서브쿼리는 SELECT문의 컬럼위치, FROM 절,WHERE 절 에 사용이 가능
- INSERT, UPDATE,CREATE문에도 사용
-- SELECT 컬럼명,(SELECT 컬럼명 FROM 테이블)
: 스칼라서브쿼리, 단일행서브쿼리
-- FROM (SELECT 컬럼명.... FROM 테이블) : 다중행, 다중행다중열 서브쿼리
-- WHERE 컬럼명 비교연산(SELECT 컬럼명 FROM 테이블명) : 단일행, 다중행, 다중열 서브쿼리
SELECT SALARY FROM EMPLOYEE WHERE EMP_NAME='윤은해';
SELECT *
FROM EMPLOYEE
WHERE SALARY=(SELECT SALARY FROM EMPLOYEE WHERE EMP_NAME='윤은해');
SELECT AVG(SALARY) FROM EMPLOYEE WHERE DEPT_CODE='D5';
SELECT E.*,(SELECT AVG(SALARY) FROM EMPLOYEE WHERE DEPT_CODE='D5') AS AVG_SAL
FROM EMPLOYEE E
WHERE SALARY>=(SELECT AVG(SALARY) FROM EMPLOYEE WHERE DEPT_CODE='D5');
서브쿼리의 결과 (RESULTSET)가 1개열 1개행인 경우를 말한다.
SELECT EMP_NAME,SALARY,DEPT_CODE
FROM EMPLOYEE E
WHERE SALARY>=(SELECT AVG(SALARY) FROM EMPLOYEE);
SELECT *
FROM EMPLOYEE
WHERE DEPT_CODE=(SELECT DEPT_ID FROM DEPARTMENT WHERE DEPT_TITLE='총무부');
SELECT *
FROM EMPLOYEE
WHERE JOB_CODE=(SELECT JOB_CODE FROM JOB WHERE JOB_NAME='과장');
서브쿼리의 결과가 1개컬럼, 다수의 행을 갖는 것이다.
SELECT JOB_CODE FROM JOB WHERE JOB_NAME IN('과장','부장');
-- 2개의 행이 나옴 (다중행)
SELECT *
FROM EMPLOYEE
WHERE JOB_CODE IN(SELECT JOB_CODE FROM JOB WHERE JOB_NAME IN('과장','부장')); -- JOB_CODE가 1개이기때문에 () 와 비교할 수 없다-> IN을 사용
다중행 서브쿼리 대소비교하기
1. ANY
- 컬럼>(=)ANY() : 다중행 서브쿼리의 결과 중 하나라도 크면 참
-> 다중행 서브쿼리 결과 중 최소값보다 크면 참- 컬럼<(=)ANY() : 다중행 서브쿼리의 결과 중 하나라도 작은면 참
-> 다중행 서브쿼리 결과 중 최대값보다 작으면 참
-> D5,D6사원의 급여보다 많이 받는 사원SELECT * FROM EMPLOYEE WHERE SALARY> ANY(SELECT SALARY FROM EMPLOYEE WHERE DEPT_CODE IN('D5','D6')); -- > 180만원 이상 받는 사원들만 출력됨SELECT * FROM EMPLOYEE WHERE SALARY< ANY(SELECT SALARY FROM EMPLOYEE WHERE DEPT_CODE IN('D5','D6')); -- > 최대값보다 작게 받는 값들이 출력됨2. ALL
- 컬럼>(=)ALL() : 다중행 서브쿼리의 결과 중 모든값보다 크면 참
->다중행 서브쿼리 결과 중 최대값보다 크면 참- 컬럼<(=)ALL() : 다중행 서브쿼리의 결과 중 모든값보다 크면 참
->다중행 서브쿼리 결과 중 최소값보다 크면 참SELECT * FROM EMPLOYEE WHERE SALARY> ALL(SELECT SALARY FROM EMPLOYEE WHERE DEPT_CODE IN('D5','D6')); -- 390만원 이상 받는 사원들이 출력됨SELECT * FROM EMPLOYEE WHERE SALARY<ALL(SELECT SALARY FROM EMPLOYEE WHERE DEPT_CODE IN('D5','D6')); -- 180만원 보다 적게 받는 사원들이 출력됨
서브쿼리의 결과가 1개행, 다수 컬럼을 갖는 것이다.
SELECT DEPT_CODE,JOB_CODE FROM EMPLOYEE WHERE ENT_YN='Y';
//위는 퇴직한 사원의 부서, 직급을 구하는 문이고 이SELECT문이 서브문으로 들어가게된다.
SELECT DEPT_CODE,JOB_CODE,EMP_NAME
FROM EMPLOYEE
WHERE (DEPT_CODE,JOB_CODE)=(SELECT DEPT_CODE,JOB_CODE FROM EMPLOYEE WHERE ENT_YN='Y');
SELECT DEPT_CODE,SALARY FROM EMPLOYEE JOIN DEPARTMENT ON DEPT_CODE=DEPT_ID
WHERE SALARY=2000000 AND DEPT_TITLE='기술지원부';
위의 SELECT문은 기술지원부이면서 급여가 200만원 이상인 사원을 골라내줌(서브쿼리문으로 들어감)
SELECT EMP_NAME,DEPT_CODE,SALARY
FROM EMPLOYEE
WHERE (DEPT_CODE,SALARY)
=(SELECT DEPT_CODE,SALARY FROM EMPLOYEE WHERE DEPT_CODE=(SELECT DEPT_ID FROM DEPARTMENT WHERE DEPT_TITLE='기술지원부')
AND SALARY=2000000);
서브쿼리의 결과가 다수행, 다수 컬럼을 갖는 것이다.
- FROM 절에 많이 사용함 -> INLINE VIEW 가상의 테이블을 만들어놓고 사용할 때를 말함
SELECT * FROM(SELECT * FROM EMPLOYEE JOIN DEPARTMENT ON DEPT_CODE=DEPT_ID JOIN JOB USING(JOB_CODE));
서브쿼리의 SELECT문을 작성할 때 메인쿼리의 값을 가져와 사용하는 구문이다.
예를 들어 본인이 속한 부서의 사원수 출력, 본인이 속한 부서의 급여 평균보다 급여를 많이 받는 사원조회하기를 하면 본인이라는 값이 바뀌면서 해당하는 값도 바뀌면서 출력/집계된다.
-- 상품에 달려있는 댓글갯수, 게시글에 달려있는 첨부파일 갯수
✋문제풀이
SELECT COUNT(*) FROM EMPLOYEE GROUP BY DEPT_CODE;
--> 본인이 속한 부서의 사원 수가 아님 그냥 부서별로 사원수를 구한것,
정확하게 상관 서브쿼리를 사용해서 풀기⤵️
SELECT EMP_NAME,DEPT_CODE,(SELECT COUNT(*)FROM EMPLOYEE WHERE DEPT_CODE=E.DEPT_CODE) AS 사원수
FROM EMPLOYEE E;
SELECT *
FROM EMPLOYEE E
WHERE (SELECT COUNT(*)FROM EMPLOYEE WHERE DEPT_CODE=E.DEPT_CODE)>=3;
이용해서 ROW를 필터할 수 있음
- 서브쿼리의 결과가 1개 이상이면 TRUE, 0개면 FALSE반환하는 연산
✋문제풀이
SELECT *
FROM EMPLOYEE E
WHERE EXISTS(SELECT 1 FROM EMPLOYEE WHERE MANAGER_ID=E.EMP_ID);
SELECT *
FROM EMPLOYEE E
WHERE NOT EXISTS(SELECT * FROM EMPLOYEE WHERE SALARY>E.SALARY);
SELECT EMP_NAME,SALARY,JOB_NAME
FROM EMPLOYEE E JOIN JOB J ON E.JOB_CODE=J.JOB_CODE
WHERE SALARY> (SELECT AVG(SALARY) FROM EMPLOYEE WHERE JOB_CODE=E.JOB_CODE);
FROM절에 서브쿼리 사용하기 -> 논리적인 가상테이블
- INLINE VIEW : 1회용으로 사용하는 구문 -> 재사용 불가능
STORED VIEW : 저장해놓고 사용하는 구문 -> 재사용가능 -> VIEW 테이블
➡️ RESULTSET을 테이블처럼 이용할 수 있게 해주는 기능이다.SELECT * FROM (SELECT EMP_NAME, EMAIL, PHONE, SALARY*12 AS YEAR_SAL FROM EMPLOYEE);
ex)
SELECT E.*, DECODE(SUBSTR(EMP_NO,8,1),'1','남','2','여')
FROM EMPLOYEE E
WHERE SUBSTR(EMP_NO,8,1)=2;
⤵️
SELECT *
FROM ( SELECT E.*, DECODE(SUBSTR(EMP_NO,8,1),'1','남','2','여') AS GENDER
FROM EMPLOYEE E)
WHERE GENDER='여';
이런식으로 통계쿼리, 자주사용하는 JOIN, 집합연산결과, 가상컬럼이 추가된 SELECT문일 때 view 많이 쓰인다.
✋문제풀이
SELECT V.*,EMP_NAME,TO_CHAR(DEPT_AVG,'L999,999,999') AS DEPT_AVG
FROM ( SELECT EMP_NAME,NVL(DEPT_TITLE,'인턴') AS DEPT_TITLE, JOB_NAME, (SALARY+(SALARY*NVL(BONUS,0)))*12 AS YEAR_SAL,
(SELECT FLOOR(AVG(SALARY)) FROM EMPLOYEE WHERE E.DEPT_CODE=DEPT_CODE) AS DEPT_AVG
FROM EMPLOYEE E LEFT JOIN DEPARTMENT D ON DEPT_CODE=DEPT_ID
JOIN JOB J ON E.JOB_CODE=J.JOB_CODE) V
WHERE YEAR_SAL>=3000000;
➡️ 별칭을 설정하여 다른조건에서도 별칭으로 사용할 수 있다(테이블안에서만), 사용자에게 보여주고싶지않은 컬럼을 걸러서 설정해서 보여줄 수 있는 기능도 한다.
⤵️ 아이디에 2가 들어간 사람을 조회할 때
서브쿼리문으로 ROW를 UNION으로 합친 테이블을 만들어놓고 사용함.
SELECT *
FROM (
SELECT EMP_ID AS ID, EMP_NAME AS NAME
FROM EMPLOYEE
UNION
SELECT DEPT_ID, DEPT_TITLE
FROM DEPARTMENT
UNION
SELECT JOB_CODE, JOB_NAME
FROM JOB
)
WHERE ID LIKE '%2%' ;
인라인뷰 안에 인라인뷰를 넣을 수도 있다.⤵️
SELECT A.*
FROM (
SELECT T.*,(SELECT AVG(SALARY) FROM EMPLOYEE WHERE JOB_CODE=T.JOB_CODE) AS AVG_JOB
FROM (
SELECT SALARY*12 AS YEAR_SAL, E.*,D.*, J.JOB_CODE AS JOB_JOB_CODE, J.JOB_NAME,
(SELECT AVG(SALARY) FROM EMPLOYEE WHERE DEPT_CODE=E.DEPT_CODE) AS AVG_DEPT
FROM EMPLOYEE E JOIN DEPARTMENT D ON DEPT_CODE=DEPT_ID
JOIN JOB J ON E.JOB_CODE=J.JOB_CODE
) T
) A;
- 오라클이 기본적으로 제공하는 ROWNUM이라는 가상컬럼을 이용하는 방법
- 오라클이 제공하는 WINDOW함수 RANK()를 이용하는 방법
SELECT한 데이터에 일련번호를 붙이는 것이다. 테이블이나, 특정 집합에서 원하는 만큼의 행만 가져오고 싶을 때, 행의 개수를 제한하는 용도로 사용한다.
✋문제풀이
SELECT ROWNUM, E.*
FROM (SELECT EMP_NAME, SALARY FROM EMPLOYEE ORDER BY SALARY) E
WHERE ROWNUM <=3;
SELECT ROWNUM, A.*
FROM(
SELECT ROWNUM AS RNUM, E.*
FROM (
SELECT EMP_NAME, SALARY FROM EMPLOYEE ORDER BY SALARY
) E
) A
WHERE A.ROWNUM BETWEEN 5 AND 10;
-- ROWNUM은 중간번호의 ROW를 찾지 못함(어디가 5번이지?🤔) 그래서 한번 더 FROM 안에 씌워줘야한다. (가상컬럼으로 지정/고정값으로 만들어줌)
SELECT ROWNUM AS RNUM, E.*
FROM (SELECT EMP_NAME, SALARY FROM EMPLOYEE ORDER BY SALARY) E
WHERE ROWNUM BETWEEN 5 AND 10;
SELECT *
FROM (SELECT ROWNUM AS RNUM, E.*
FROM
(SELECT EMP_NAME,DEPT_TITLE,JOB_NAME, SALARY
FROM EMPLOYEE
LEFT JOIN DEPARTMENT ON DEPT_CODE=DEPT_ID
JOIN JOB USING(JOB_CODE)
ORDER BY SALARY DESC)E)
-- WHERE RNUM BETWEEN 1 AND 3;
WHERE RNUM BETWEEN 10 AND 15;
✋문제풀이
SELECT *
FROM ( SELECT RANK() OVER(ORDER BY SALARY) AS "ORDER",
DENSE_RANK() OVER(ORDER BY SALARY) AS "ORDER2",
E.*
FROM EMPLOYEE E
)
WHERE "ORDER" BETWEEN 5 AND 10;
SELECT *
FROM (
SELECT DEPT_TITLE, AVG(SALARY), RANK() OVER(ORDER BY AVG(SALARY) DESC) AS AVG_ORDER
FROM EMPLOYEE
LEFT JOIN DEPARTMENT ON DEPT_CODE=DEPT_ID
GROUP BY DEPT_TITLE
)
WHERE AVG_ORDER BETWEEN 1 AND 3;
한개 SELECT문안에서 지정된 SELECT문을 재사용하기 위해 사용한다.
이름이 부여된 서브쿼리이며,
임시테이블을 만든다는 관점에서보면 VIEW와 쓰임새가 비슷한데
차이점이 있다면 VIEW는 한번 만들어놓으면 DROP 할 때까지 없어지지 않지만 WITH절의 경우 한번 실행할 쿼리문내에 정의되어 있을 경우 그 쿼리문안에서만 실행된다는 차이점이 있다.
WITH
TEST_WITH AS (SELECT EMP_NAME, SALARY FROM EMPLOYEE ORDER BY SALARY DESC),
TEST_WITH2 AS (SELECT * FROM EMPLOYEE)
SELECT EMP_NAME,SALARY FROM TEST_WITH
UNION
SELECT EMAIL, BONUS
FROM TEST_WITH2;
점점 어려워지고있는 .. ㅜㅜ 하지만 이겨내봅시당 !😊