[oracle] 오라클 SQL - 서브쿼리
오라클 SQL 서브쿼리
7장. 서브쿼리
7장 : 서브쿼리란?
다른 SQL에 포함된 SELECT!
서브쿼리 분류
- Single colunmn Single row subquery : >, >=, <, <=, = … (왼쪽, 오른쪽 값이 1개일 때)
- Single colunmn Multiple row subquery : in(왼쪽 값 1개, 오른쪽 값 여러개)
- Multiple column Multiple row subquery : (pair-wise subquery)
- Inline view : from절의 select
- Correlated subquery : 상호관련 서브쿼리(내부 테이블에 외부 컬럼이 들어와있는 경우)
문제
- 7782 사원보다 급여를 많이 받으면서 7902 사원과 같은 직무를 수행하는 사원?
--하나 하나 찾아서 처리하는 방법 select sal from emp where empno = 7782; --2450 select job from emp where empno = 7902; --ANALYST select empno, ename, job, sal from emp where sal > 2450 and job = 'ANALYST';
--조인으로 처리하는 방법
select e.empno, e.ename, e.job, e.sal
from emp e, emp a, emp b
where a.empno = 7782
and b.empno = 7902
and b.sal > a.sal
and e.job = b.job;
--서브쿼리로 처리하는 방법
--하나하나 처리하는 것에서
--A
select sal
from emp
where empno = 7782;
--B
select job
from emp
where empno = 7902;
--A+B
select empno, ename, job, sal
from emp
where sal > (A)
and job = (B);
--정리하면 이렇게
select empno, ename, job, sal
from emp
where sal > (select sal
from emp
where empno = 7782)
and job = (select job
from emp
where empno = 7902);
[참고] Query Transformation 기능을 갖고 있다.
- where ename like ‘SCOTT’ –> like의 의미가 무의미하기 때문에 오라클에서는 where ename = ‘SCOTT’ 으로 자동 변경
- where sal between 1000 and 2000 –> where sal >= 1000 and sal <= 2000
- where job in (‘ANALYST’, ‘MANAGER’) –> where job = ‘ANALYST’ OR ‘MANAGER’
- 그 밖에 서브쿼리를 날리면 조인으로 자동 처리된다. 등등등~
문제
- 회사의 평균 급여보다 많은 급여를 받으면서 7788 사원보다 빨리 입사한 사원을 쿼리하세요. 단 서브쿼리 문법을 활용하도록 하세요.
--정답 select empno, sal, hiredate from emp where sal > (select avg(sal) from emp) and hiredate < (select hiredate from emp where empno = 7788); -- +@ 인원수 구하기 select count(*) from emp where sal > (select avg(sal) from emp) and hiredate < (select hiredate from emp where empno = 7788); -- +@ 쭉쭉 추가할 수 있다. select deptno, job, count(*) from emp where sal > (select avg(sal) from emp) and hiredate < (select hiredate from emp where empno = 7788) group by deptno, job order by deptno, job;
문제
- 최저 급여자의 ename, sal을 쿼리하세요.
--정답 select empno, ename, sal from emp where sal = (select min(sal) from emp);
문제
- EMPNO, ENAME, SAL, “회사평균급여”를 쿼리하세요. 단, 서브쿼리 문법을 활용하도록 하세요.
- (Scalar subquery 사용법을 눈여겨 볼 것)
select empno, ename, sal, (select avg(sal) from emp) 평균급여 from emp;
문제
- EMPNO, ENAME, SAL, “소속부서평균급여”를 쿼리하세요. 단, 서브쿼리 문법을 활용하도록 하세요.
- (Scalar subquery + 상호관련 서브쿼리 사용법을 눈여겨 볼 것)
select empno, ename, sal, deptno, (select avg(sal) from emp where deptno = e1.deptno) 소속부서평균급여 from emp e1;
문제
- EMPNO, ENAME, SAL, “소속부서평균급여”를 쿼리하세요. 단, 서브쿼리 문법을 활용하도록 하세요.
- (Scalar subquery 사용법을 눈여겨 볼 것)
select empno, ename, deptno from emp e1 order by (select loc from dept where deptno = e1.deptno); 이 문제를 조인으로 해결하면 아래와 같습니다. select e.empno, e.ename, e.deptno from emp e, dept d where e.deptno = d.deptno order by d.loc;
문제
- 근무하는 사원이 있는 부서의 deptno, dname, loc를 쿼리하세요.
- (하나의 문제를 다양한 방법으로 해결하는 것을 전하는 예제임. 그런데 특히 상호관련 서브쿼리, Exists 연산자 사용법을 눈여겨 볼 것)
--에러 --= 왼쪽 값 1개 오른쪽 값 1개 그래서 에러 select deptno, dname, loc from dept where deptno = (select distinct deptno from emp); --서브쿼리 안에서는 distinct를 안 써도 중복값을 제거하고 리턴한다. --정답(일반 쿼리) --in 왼쪽 값 1개 오른쪽 값 여러개 select deptno, dname, loc from dept where deptno in (select distinct deptno from emp); --서브쿼리 안에서는 distinct를 안 써도 중복값을 제거하고 리턴한다. --정답(상호 관련 서브쿼리 처리) --근무하는 사원이 있는 부서를 상호 관련 서브쿼리로 해결 select deptno, dname, loc from dept d where 0 < (select count(*) from emp where deptno = d.deptno); --full scan --수만명 중에 수천명을 찾아서 구하는 방식 --한번이라도 팔린적 있는 물건 --+@ exists 사용(상호관련 서브쿼리+Exists) select deptno, dname, loc from dept d where exists (select 'x' from emp where deptno = d.deptno); --관습적으로 'x' or 1을 넣는다. --dept scan, emp 부분 scan. --존재 유무를 파악할 때 exists를 사용하는게 좋다.
문제
- 부하직원이 있는 사원을 쿼리하세요.
- (Exists 연산자 사용법을 눈여겨 볼 것)
--내 사원번호가 MGR에 있어야 = 부하직원이 있는 select empno, ename, job, sal from emp where empno in (select mgr from emp); select empno, ename, job, sal from emp e1 where exists (select 'x' from emp where mgr = e1.empno);
- 5명 이상 근무하는 부서를 상호 관련 서브쿼리로 해결하세요.
- (상호관련 서브쿼리 사용법을 눈여겨 볼 것)
select deptno, dname, loc from dept d where 5 <= (select count(*) from emp where deptno = d.deptno);
- 부하 직원이 3명 이상인 사원을 쿼리하세요.
- (상호관련 서브쿼리 사용법을 눈여겨 볼 것)
select empno, ename, job, sal from emp e1 where 3 <= (select count(*) from emp where mgr = e1.empno);
문제
- empno, ename, sal, 급여의 누적합을 쿼리 하세요. 단 서브 쿼리 문법을 활용하도록 하세요.
- (상호관련 서브쿼리 사용법을 눈여겨 볼 것)
--정답 select empno, ename, sal, (select sum(sal) from emp where empno <= e1.empno) as 누적합 from emp e1;
문제
- empno, ename, hiredate, “먼저입사한사원수”를 쿼리 하세요. 단 서브 쿼리 문법을 활용하도록 하세요.
- (상호관련 서브쿼리 사용법을 눈여겨 볼 것)
--정답 select empno, ename, hiredate, (select count(hiredate) from emp where hiredate < e1.hiredate) as 먼저입사한사원수 from emp e1 order by hiredate
문제
- Single coulumn Multiple row subquery와 부등호 연산을 이해하도록 하세요.
drop table t1 purge; drop table t2 purge; create table t1 (col1 number); insert into t1 values (1000); insert into t1 values (2000); insert into t1 values (3000); commit; create table t2 (col1 number); insert into t2 values (1500); insert into t2 values (2500); commit; select * from t1; select * from t2; --부등호 연산자는 1:1 관계를 비교함 그래서 t1(3행), t2(2행)이므로 오류가 발생 select * from t1 where col1 > (select col1 from t2); --subquary에 any를 쓰면 비교 가능 select * from t1 where col1 > any (select col1 from t2); --크다 any, 서브쿼리에서 값을 여러개 리턴할때 서브쿼리에 MIN으로 비교해서 리턴하면 된다. select * from t1 where col1 > (select min(col1) from t2); --작다 any, 서브쿼리에서 값을 여러개 리턴할때 서브쿼리에 MAX으로 비교해서 리턴하면 된다. select * from t1 where col1 < (select min(col1) from t2); --subquary에 all을 쓰면 비교 가능 select * from t1 where col1 > all (select col1 from t2); --모두보다 크다, 서브쿼리에서 값을 여러개 리턴할때 서브쿼리에 MAX으로 비교해서 리턴하면 된다. select * from t1 where col1 > (select max(col1) from t2); --모두보다 작다, 서브쿼리에서 값을 여러개 리턴할때 서브쿼리에 MIN으로 비교해서 리턴하면 된다. select * from t1 where col1 < (select min(col1) from t2);
문제
- Multiple coulumn Multiple row subquery 예제를 이해하도록 하세요.
drop table t1 purge; drop table t2 purge; create table t1 (col1 number, col2 varchar2(10)); insert into t1 values (100, 'A'); insert into t1 values (100, 'B'); insert into t1 values (200, 'A'); insert into t1 values (200, 'B'); create table t2 (col1 number, col2 varchar2(10)); insert into t2 values (100, 'A'); insert into t2 values (200, 'B'); commit; select * from t1; select * from t2; -> non-pair-wise subquery select * from t1 where col1 in (select col1 from t2) /* 100, 200 */ and col2 in (select col2 from t2); /* A, B */ -> pair-wise subquery select * from t1 where (col1, col2) in (select col1, col2 from t2); /* (100, A), (200, B) */
문제
- Subquery Factoring을 이해하도록 합시다.
--본문을 시작하기 전에 미리 query를 만들어서 시작. --문장을 단순하게 만든다. --서브쿼리 결과를 임시집합으로 생성해서 성능을 개선함. with a as (select deptno, avg(sal) dept_avgsal from emp group by deptno), --부서별 평균급여 b as (select avg(sal) emp_avg_sal from emp) --급여별 평균급여 select e.empno, e.ename, e.sal, a.dept_avgsal, b.emp_avg_sal from emp e, a, b where e.deptno = a.deptno and a.dept_avgsal = (select max(dept_avgsal) from a);
References
개발자님들 덕분에 많이 배울 수 있었습니다. 감사의 말씀 드립니다.