- Join vs Subquery의 차이는 무엇인가?
sql을 공부하면서 join, subquery를 둘 다 사용해보았다.
성능적으로 어떤게 더 나은가? 가 궁금해졌다.
가독성은 subquery가 좋았는데 나중에 조회할 데이터가 많아졌을 때 성능이 떨어지지 않을까 생각이 들었다.
그래서 관련 자료를 한번 찾아보았다.
자료들을 찾아보니 join, subquery 둘 다 성능적으로 좋다고 할 수는 없으나 경우에 따라 어떤 경우에는 subquery가 좋을 수 있고, 어떤 경우에는 join이 좋을 수도 있다고 한다. 그래서 이걸 파악하기 위해서는 실무에서 많은 쿼리문을 접해봐야 될 것 같다.
+) join은 통계성 데이터에 subquery는 한 테이블에서 하나만 가지고 오고 싶을 때 주로 사용한다고 한다.
그리고 까먹지 않기 위해 아래 내용을 정리해보았다.
두 개 이상의 테이블에 있는 컬럼의 값을 한번에 가져오기 위해 사용하는 것이 조인
select 컬럼명 from 테이블1, 테이블2;
두 개 이상의 테이블을 조인하게 되면 다대다의 관계로 가져오기 때문에
테이블1의 로우의 수 x 테이블2의 로우의 수 만큼 로우를 가져오게 된다.
두 개 이상의 테이블에서 가져온 결과 중에 정확한 결과면 가져오기 위해 공통 부분을 이용한 조건문이 반드시 필요함
현업에서 많이 사용함
대표적으로 Inner Join, Outer Join, Self Join이 있음
-- 사원 테이블(emp)과 부서 테이블(dept)을 join함
select * from emp; // 14개 행
select * from dept; // 4개 행
select * from emp, dept; // 56개 행
select *
from emp a1, dept a2
where a1.deptno = a2.deptno;
-- 사원의 사원번호, 이름, 근무부서 이름을 가져온다.
select a1.empno, a1.ename, a2.dname
from emp a1, dept a2
where a1.deptno = a2.deptno;
-- 사원의 사원번호, 이름, 근무지역을 가져온다.
select a1.empno, a1.ename, a2.loc
from emp a1, dept a2
where a1.deptno = a2.deptno;
-- DALLAS에 근무하고 있는 사원들의 사원번호, 이름, 직무를 가져온다.
select a1.empno, a1.ename, a1.job
from emp a1, dept a2
where a1.deptno = a2.deptno and a2.loc = 'DALLAS';
-- SALES 부서에 근무하고 있는 사원들의 급여 평균을 가져온다.
select avg(a1.sal)
from emp a1, dept a2
where a1.deptno = a2.deptno and a2.dname = 'SALES';
-- 1982년에 입사한 사원들의 사원번호, 이름, 입사일, 근무부서 이름을 가져온다.
select a1.empno, a1.ename, a1.hiredate, a2.dname
from emp a1, dept a2
where a1.deptno = a2.deptno and a1.hiredate between '1982-01-01' and '1982-12-31';
-- 각 사원들의 사원번호, 이름, 급여, 급여등급을 가져온다.
select a1.empno, a1.ename, a1.sal, a2.grade
from emp a1, salgrade a2
where a1.sal between a2.losal and a2.hisal;
-- SALES 부서에 근무하고 있는 사원의 사원번호, 이름, 급여등급을 가져온다.
-- join 해야할 테이블이 3개
select a1.empno, a1.ename, a2.grade
from emp a1, salgrade a2, dept a3
where a1.sal between a2.losal and a2.hisal and a1.deptno = a3.deptno
and a3.dname = 'SALES';
-- 각 급여 등급별 급여의 총합과 평균, 사원의 수, 최대급여, 최소급여를 가져온다.
select sum(a1.sal), trunc(avg(a1.sal)), count(a1.sal), max(a1.sal), min(a1.sal)
from emp a1, salgrade a2
where a1.sal between a2.losal and a2.hisal
group by a2.grade;
-- 급여 등급이 4등급인 사원들의 사원번호, 이름, 급여, 근무부서이름, 근무지역을 가져온다.
select a1.empno, a1.ename, a1.sal, a3.dname, a3.loc
from emp a1, salgrade a2, dept a3
where a1.sal between a2.losal and a2.hisal and a1.deptno = a3.deptno
and a2.grade = 4;
-- SMITH 사원의 사원번호, 이름, 직속상관 이름을 가져온다.
-- a1 : SMITH 사원의 정보
-- a2 : 직속상관의 정보
select a1.empno, a1.ename, a2.ename
from emp a1, emp a2
where a1.mgr = a2.empno and a1.ename = 'SMITH';
-- FORD 사원 밑에서 일하는 사원들의 사원번호, 이름, 직무를 가져온다.
-- a1 : FORD의 정보
-- a2 : 부하 직원의 정보
select a2.empno, a2.ename, a2.job
from emp a1, emp a2
where a1.empno = a2.mgr and a1.ename = 'FORD';
-- SMITH 사원의 직속상관과 동일한 직무를 가지고 있는 사원들의 사원번호, 이름, 직무를 가져온다.
-- a1 : SMITH 사원의 정보
-- a2 : SMITH의 직속상관 정보
-- a3 : 직속상관과 동일한 직무를 가지고 있는 사원의 정보
select a3.empno, a3.ename, a3.job
from emp a1, emp a2, emp a3
where a1.mgr = a2.empno and a2.job = a3.job and a1.ename = 'SMITH';
-- 각 사원의 이름, 사원번호, 직장상사 이름을 가져온다. 단 직속상관이 없는 사원도 가져온다.
-- a1 : 각 사원의 정보
-- a2 : 직장상사의 정보
select a1.ename, a1.empno, a2.ename
from emp a1, emp a2
where a1.mgr = a2.empno(+); // null인 부분까지 포함시킬 경우 (+) 해줌, 부족한 부분에 +
-- 모든 부서의 소속 사원의 근무부서명, 사원번호, 사원이름, 급여를 가져온다.
select a2.dname, a1.empno, a1.ename, a1.sal
from emp a1, dept a2
where a1.deptno(+) = a2.deptno; // null인 부분까지 포함시킬 경우 (+) 해줌, 부족한 부분에 +
-- SCOTT 사원이 근무하고 있는 부서의 이름을 가져온다.
select dname
from dept
where deptno = (select deptno
from emp
where ename = 'SCOTT');
select a2.dname
from emp a1, dept a2
where a1.deptno = a2.deptno and a1.ename = 'SCOTT';
-- SMITH와 같은 부서에 근무하고 있는 사원들의 사원번호, 이름, 급여액, 부서이름을 가져온다.
select a1.empno, a1.ename, a1.sal, a2.dname
from emp a1, dept a2
where a1.deptno = a2.deptno and a1.deptno = (select deptno
from emp
where ename = 'SMITH');
-- MARTIN과 같은 직무를 가지고 있는 사원들의 사원번호, 이름, 직무를 가져온다.
select empno, ename, job
from emp
where job = (select job
from emp
where ename = 'MARTIN');
-- ALLEN과 같은 직속상관을 가진 사원들의 사원번호, 이름, 직속상관이름을 가져온다.
-- a1 : 사원의 정보
-- a2 : 직속상관 정보
select a1.empno, a1.ename, a2.ename
from emp a1, emp a2
where a1.mgr = a2.empno and a1.mgr = (select mgr
from emp
where ename = 'ALLEN');
-- WARD와 같은 부서에 근무하고 있는 사원들의 사원번호, 이름, 부서번호를 가져온다.
select empno, ename, deptno
from emp
where deptno = (select deptno
from emp
where ename = 'WARD');
-- SALESMAN의 평균 급여보다 많이 받는 사원들의 사원번호, 이름, 급여를 가져온다.
select empno, ename, sal
from emp
where sal > (select avg(sal)
from emp
where job = 'SALESMAN');
-- DALLAS 지역에 근무하는 사원들의 평균 급여를 가져온다.
select trunc(avg(sal))
from emp
where deptno = (select deptno
from dept
where loc = 'DALLAS');
-- SALES 부서에 근무하는 사원들의 사원번호, 이름, 근무지역을 가져온다.
select a1.empno, a1.ename, a2.loc
from emp a1, dept a2
where a1.deptno = a2.deptno and a1.deptno = (select deptno
from dept
where dname = 'SALES');
-- CHICAGO 지역에 근무하는 사원들 중 BLAKE이 직속상관인 사원들의 사원번호, 이름, 직무를 가져온다.
select empno, ename, job
from emp
where mgr = (select empno
from emp
where ename = 'BLAKE')
and deptno = (select deptno
from dept
where loc = 'CHICAGO');
서브쿼리를 통해 가져온 결과가 하나 이상인 경우 결과를 모두 만족하거나 결과 중 하나만 만족하거나 해야 하는 경우가 있음
이때 다음과 같은 연산자를 사용하면 됨
IN
: 서브쿼리의 결과 중 하나라도 일치하면 조건은 참이 된다.
ANY, SOME
: 서브쿼리의 결과와 하나 이상 일치하면 조건은 참이 된다.
ALL
: 서브쿼리의 결과와 모두 일치해야 조건은 참이 된다.
-- 3000 이상의 급여를 받는 사원들과 같은 부서에 근무하고 있는 사원의 사원번호, 이름, 급여를 가져온다.
select empno, ename, sal
from emp
where deptno in (select deptno
from emp
where sal >= 3000);
-- 직무가 CLERK인 사원과 동일한 부서에 근무하고 있는 사원들의 사원번호, 이름, 입사일을 가져온다.
select empno, ename, hiredate
from emp
where deptno in (select deptno
from emp
where job = 'CLERK');
-- KING을 직속상관으로 가지고 있는 사원들이 근무하고 있는 근무 부서명, 지역을 가지고 온다.
select dname, loc
from dept
where deptno in (select deptno
from emp
where mgr = (select empno
from emp
where ename = 'KING'));
-- CLERK들의 직속상관의 사원번호, 이름, 급여를 가져온다.
select empno, ename, sal
from emp
where empno in (select mgr
from emp
where job = 'CLERK');
-- 각 부서별 급여 평균보다 더 많이 받는 사원의 사원번호, 이름, 급여를 가져온다.
select empno, ename, sal
from emp
where sal > all (select avg(sal)
from emp
group by deptno);
-- 각 부서별 급여 평균보다 더 많이 받는 사원의 사원번호, 이름, 급여를 가져온다.
select empno, ename, sal
from emp
where sal > all (select avg(sal)
from emp
group by deptno);
select empno, ename, sal
from emp
where sal > (select max(avg(sal))
from emp
group by deptno);
-- 각 부서별 급여 최저치보다 많이 받는 사원들의 사원번호, 이름, 급여를 가져온다.
select empno, ename, sal
from emp
where sal > all (select min(sal)
from emp
group by deptno);
select empno, ename, sal
from emp
where sal > (select max(min(sal))
from emp
group by deptno);
-- SALESMAN 보다 급여를 적게 받는 사원들의 사원번호, 이름, 급여를 가져온다.
select empno, ename, sal
from emp
where sal < all (select sal
from emp
where job = 'SALESMAN');
select empno, ename, sal
from emp
where sal < (select min(sal)
from emp
where job = 'SALESMAN');
-- 각 부서별 최저 급여 액수보다 많이 받는 사원들이 사원번호, 이름, 급여를 가져온다.
select empno, ename, sal
from emp
where sal > any (select min(sal)
from emp
group by deptno);
-- DALLAS에 근무하고 있는 사원들 중 가장 나중에 입사한 사원의 입사 날짜보다 더 먼저 입사한 사원들의 사원번호, 이름, 입사일을 가져온다.
select empno, ename, hiredate
from emp
where hiredate < any (select hiredate
from emp
where deptno = (select deptno
from dept
where loc = 'DALLAS'));