조회된 결과에 행 번호를 지정하여 붙여주는 키워드 👉🏻 조회결과에 가상컬럼으로 추가됨
가상컬럼 : 실제 테이블에는 없지만 조회할 때 가상으로 만들어 추가되는 컬럼
*과 함께 사용시 E.*과 같이 Alias를 붙여서 작성해야 함SELECT rownum, empno, ename
FROM emp
WHERE deptno = 30;
-- 잘못된 사용법
SELECT
rownum rnum
-- , * --rownum 키워드로 모든 행에 번호데이터가 채워지기 전에 * 기호가 rownum컬럼을 추가하여 조회하라는 뜻이 됨
, E.* --컬럼값이 이미 채워져있는 테이블의 모든 컬럼(E.*)로 변경
FROM emp E;
-- 잘못된 사용법
-- rownum이 다 붙은 후에 정렬이 실행됨 -> 서브쿼리로 정렬 먼저 수행
SELECT
rownum sal_rank
, E.*
FROM emp
ORDER BY sal DESC NULLS LAST;
SELECT
rownum sal_rank
, E.*
FROM (
SELECT * FROM emp ORDER BY sal DESC NULLS LAST
)E;
-- 잘못된 사용법
-- rownum은 1부터 1씩 증가시키면서 행번호를 붙임 -> 서브쿼리로 rownum부터 실행
SELECT rownum, E.* FROM emp E
WHERE rownum <=5; --1부터 5가 될 때까지 차례대로 출력 가능
--WHERE rownum BETWEEN 3 and 5; --1보다 큰 행번호부터 조회할 수 없음
SELECT * FROM (
SELECT rownum rnum, E.* FROM emp E
)
WHERE rnum BETWEEN 3 and 5;
조회 결과에 순위(랭킹)를 적용하여 상위 또는 하위 데이터를 찾아내는 것 👉🏻 서브쿼리를 이용하여 원하는 순위의 데이터 추출
-- 급여를 많이 받는 7명
SELECT * FROM (
SELECT rownum rnum, E.* FROM ( --행번호 부여(급여 랭킹 순위 부여)
SELECT * FROM emp
WHERE deptno IN(20, 30) --조회 조건 부여
ORDER BY sal DESC NULLS LAST --급여순 내림차순 정렬
) E
) R
WHERE rnum BETWEEN 1 AND 7; --TOP 7 적용
ORDER BY, GROUP BY를 적용하는 구문을 작성할 때 서브쿼리를 사용해야만 하는 상황을 개선하기 위한 함수
PARTITION BY : GROUP BY처럼 사용-- sal기준으로 정렬, 각 행의 누적된 합계를 구하기
SELECT
empno, ename, sal, deptno
, sum(sal) OVER( ORDER BY sal DESC NULLS LAST) sum_sal
FROM emp;
-- deptno기준으로 그룹화, 그룹별 합계
SELECT
empno, ename, sal, deptno
, sum(sal) OVER( PARTITION BY deptno) sum_sal
FROM emp;
-- deptno기준으로 그룹핑, 그룹별 sal기준 정렬하여 누적합계
SELECT
empno, ename, sal, deptno
, sum(sal) OVER( PARTITION BY deptno ORDER BY sal DESC NULLS LAST) sum_sal
FROM emp;
ROW_NUMBER()
조회된 순서에 맞게 행 번호를 부여
RANK()
같은 값이면 같은 등수를 부여하고 같은 등수의 개수만큼 다음 순위를 건너뜀
1 2 2 2 2 6 7 8
DENSE_RANK()
같은 값이면 같은 등수를 부여하고 같은 등수의 개수와 상관없이 바로 다음 순위를 순서대로 지정
1 2 2 2 2 3 4 5
SELECT
row_number() OVER( ORDER BY sal DESC NULLS LAST, ename) rnum
, rank() OVER( ORDER BY sal ASC ) rank
, dense_rank() OVER( ORDER BY sal ASC) dense_rank
, empno, ename, sal, deptno
FROM emp;
서브쿼리의 조회 결과가 2행(2건) 이상 나오는 서브쿼리
👉🏻 스칼라 서브쿼리에서는 적용불가
👉🏻 인라인 뷰에서는 사용가능
SELECT * FROM emp
WHERE sal IN (
SELECT DISTINCT sal
FROM emp
WHERE job = 'SALESMAN'
-- ORDER BY sal --서브쿼리에서 ORDER 사용 불가
);
SELECT * FROM emp
--WHERE sal = ANY ( --IN연산자와 같은 동작
--WHERE sal < ANY ( --최대값보다 작은 데이터 조회
WHERE sal > ANY ( --최소값보다 큰 데이터 조회
SELECT DISTINCT sal
FROM emp
WHERE job = 'SALESMAN'
);
SELECT * FROM emp
--WHERE sal = ALL ( --조회되는 데이터 없음
--WHERE sal < ALL ( --최소값보다 작은 데이터 조회
WHERE sal > ALL ( --최대값보다 큰 데이터 조회
SELECT DISTINCT sal
FROM emp
WHERE job = 'SALESMAN'
);
SELECT * FROM emp E
WHERE EXISTS (
SELECT *
FROM emp M
WHERE E.empno = M.mgr
);
💡 IN 연산자 vs EXISTS 연산자
IN연산자는 서브쿼리를 수행하고 조회될 행을 중간 데이터로 생성하고 메인쿼리와 직접 비교해봄
EXISTS연산자는 메인쿼리와 서브쿼리가 서로 연결되는 데이터가 존재하는지 확인만 하고 중간 데이터를 생성하지 않음
👉🏻 EXISTS는 비교를 위한 중간데이터를 생성하지 않아 IN보다 수행속도가 빠른 편임
두개의 쿼리(SELECT)의 결과 집합(Result Set)을 하나의 결과 집합으로 만드는 연산자
❗ 각 SELECT 구문의 결과의 컬럼수, 컬럼이름, 자료형이 같아야 함
UNION : 합집합 / 중복행을 허용하지 않음
UNION ALL : 합집합 / 중복행을 허용
INTERSECT : 교집합
MINUS : 차집합
-- UNION : 합집합 / 중복행 허용하지 않음
SELECT empno, ename, job, deptno FROM emp --14명
WHERE sal < 3000
UNION
SELECT empno, ename, job, deptno FROM emp --10명
WHERE sal < 2000
ORDER BY empno;
-- UNION ALL : 합집합 / 중복행 허용
SELECT empno, ename, job, deptno FROM emp --14명
WHERE sal < 3000
UNION ALL
SELECT empno, ename, job, deptno FROM emp --10명
WHERE sal < 2000
ORDER BY empno;
-- INTERSECT : 교집합
SELECT empno, ename, job, deptno FROM emp --14명
WHERE sal < 3000
INTERSECT
SELECT empno, ename, job, deptno FROM emp --10명
WHERE sal < 2000
ORDER BY empno;
-- MINUS : 차집합 -> 쿼리의 순서가 중요!
SELECT empno, ename, job, deptno FROM emp --14명
WHERE sal < 3000
MINUS
SELECT empno, ename, job, deptno FROM emp --10명
WHERE sal < 2000
ORDER BY empno;