8일차에 배운 내용을 정리하였다.
목차는 다음과 같다. :
01. 개념
02. 목적
03. 종류
-01 위치에 따른 종류
-02 서브쿼리의 형태에 따른 종류
-03 메인쿼리와의 독립성 여부에 따른 종류
01. 개념
쿼리 안의 쿼리를 뜻한다. (외부쿼리(메인쿼리) 내의 내부쿼리(서브쿼리))
메인쿼리와 구분하기 위하여 반드시 괄호로 묶어 전달해야 하며, select, insert, delete, update 문 등에서 사용한다.
02. 목적
타 언어의 경우 변수를 선언하여 다른 코드에 해당 변수를 삽입하는 구조로 되어있는 것에 비해 (R : a<-1+1, Python : a=1+1) sql는 변수의 선언이 어렵다.
따라서 서브쿼리를 이용하여 먼저 실행이 필요한 쿼리의 결과를 다시 쿼리에 전달하기 위해 사용한다.
03. 종류
-01 위치에 따른 종류
group by 절을 제외한 모든 절에 사용 가능하다.
예)
select ... (select... from...)
from ... (select... from...)
where ... (select... from...)
1) select ... (select... from...) : 스칼라 서브쿼리
2) from ... (select... from...) : 인라인 뷰
테이블은 저장공간을 차지하고 있는 객체(디스크 영역을 차지하고 있는 객체)이며, 보안의 목적 등의 이유로 사용자에게 원본 테이블 전체가 아닌 select 문을 통한 가상의 테이블을 보여준다.
사용자들은 뷰를 통해 테이블을 조회하며 insert, select 등을 수행할 수도 있다.
또한 뷰는 원본테이블의 대체 역할이기에 저장공간을 차지하지 않는다.
인라인 뷰는 쿼리 내 가상의 테이블과 비슷한 개념이다.
3) where ... (select... from...) : 서브쿼리 (일반적으로 지칭되는 서브쿼리)
-02 서브쿼리의 형태에 따른 종류
1) 단일행 서브쿼리
서브쿼리의 결과가 단 하나의 행을 리턴하는 경우이다. 대소비교, 일치비교 등 연산자 사용이 가능하다.
예) emp 테이블을 사용하여 평균급여보다 높은 급여를 받는 직원의 이름, 급여를 출력한다.
--step1) 평균급여 확인
select avg(sal) from emp;

급여의 평균이 단일행의 형태로 출력되었다.
--step2) 위에서 구한 평균급여를 기준으로 쿼리 작성
select ename, sal
from emp
where sal > 2073;

급여의 평균은 2073임을 확인하였고, 2073보다 많은 급여를 받는 직원이 조회되었으나 급여의 인상 혹은 직원의 입퇴사 등으로 인해 평균값은 계속해서 변경될 수 있다.
--step3) 위 두 쿼리 결합
select ename, sal
from emp
where sal > (select avg(sal)
from emp);

따라서 step 1에서 작성한 쿼리를 step2 의 where절에 서브쿼리로 결합하였다.
동일한 결과임을 확인할 수 있다.
2) 다중행 서브쿼리
서브쿼리 결과가 둘 이상의 행을 리턴하는 경우이다.
일치비교, 대소비교가 불가능하기에 서브쿼리가 하나의 행으로 리턴되도록 수정하거나, 연산자를 수정해야 한다.
예) emp 테이블에서 이름이 a로 시작하는 직원과 같은 부서에 근무하는 직원 출력(이름이 a로 시작되는 직원 포함)
이름이 A로 시작하는 직원의 부서번호 조회
select deptno
from emp
where ename like 'A%';

이름이 A로 시작하는 직원들은 20번, 30번 부서에 근무함을 확인하였다.
select *
from emp
where deptno = (select deptno from emp where ename like 'A%'); -- error
-- ora-01427 단일 행 하위 질의에 2개 이상의 행이 리턴되었습니다.

그러나 다중행 서브쿼리는 일치비교, 대소비교가 불가능하기에 '='를 사용하면 위와 같은 에러가 발생한다.
select *
from emp
where deptno in (select deptno from emp where ename like 'A%');

따라서 'in'을 이용하여 서브쿼리 내 조건에 부합하는 deptno를 가진 열을 조회해야 한다.
또한 다중행 서브쿼리는 일치비교, 대소비교 대신 다중행 서브쿼리 연산자 이용을 할 수 있으며, 종류는 다음과 같다. :
--IN
--ANY
--ALL
--EXISTS
--NOT EXISTS
* all과 any 연산자
서브쿼리 결과를 하나로 요약하며, 대소비교 연산자와 함께 사용시 최댓값 혹은 최솟값을 리턴한다.
> any(100, 300) : 최소(100)보다 큰
- 100보다 크거나, 300보다 커야한다. => 100 초과
< any(100, 300) : 최대(300)보다 작은
- 100보다 작거나, 300보다 작아야한다. => 300 미만
select *
from emp
where sal > any(select sal
from emp
where deptno = 10);

10번 부서(1300, 2450, 5000) 소속의 직원이 받는 그 어떤 값보다도 많은 급여를 받는 모든 직원이 조회되었다.
또한 = any 사용 시 in 연산자와 같은 의미를 가진다.
select *
from emp
where sal = any(select sal
from emp
where deptno = 10);

10번 부서(1300, 2450, 5000) 소속의 직원이 받는 급여들 중 그 어떤 값이라도 일치하는 급여를 받는 모든 직원이 조회되었다.
> all(100, 300) : 최대(300)보다 큰
- 100보다도 크고, 300보다도 커야한다. => 300 초과
- 따라서 '크다'와 all이 나오면 '최댓값'이 나옴
< all(100, 300) : 최소(100)보다 작은
- 100보다도 작고, 300보다도 작아야한다. => 100 미만
- 따라서 '작다'와 all이 나오면 '최솟값'이 나옴
select *
from emp
where sal = all(select sal
from emp
where deptno = 10);

= all의 경우 10번 부서(1300, 2450, 5000) 소속의 직원이 받는 급여를 한 사람이 '모두' 받는 조건으로 조회하기에, 공집합이 출력된다.
* exists / not exists
all or nothing으로 모두 출력 혹은 아무것도 출력하지 않는다.
- exists : 서브쿼리 결과가 참이면 메인쿼리 결과를 모두 출력하며, 거짓이면 모두 생략한다.
- not exists : 서브쿼리 결과가 거짓이면 메인쿼리 결과를 모두 출력하며, 참이면 모두 생략한다.
서브쿼리의 참/거짓만 판단하기 때문에 비교 연산자 없이 바로 exists가 작성된다.
-- exists)
select *
from emp
where deptno = 10
and exists (select 'x' -- 서브쿼리 결과가 참이므로 메인쿼리 결과가 모두 나옴
from emp);

select *
from emp
where deptno = 10
and exists (select '1' -- 서브쿼리의 select 절에는 아무거나 전달 가능(해석 의미 없음)
from emp
where 1=2); -- 항상 거짓(1=2)이므로 서브쿼리 결과가 아무것도 출력되지 않는다. (공집합 출력)

-- not exists)
select *
from emp
where deptno = 10
and not exists (select 'x'
from emp);

select *
from emp
where deptno = 10
and not exists (select '1' -- 서브쿼리의 select 절에는 아무거나 전달 가능(해석 의미 없음)
from emp
where 1=2); -- 항상 거짓(1=2)이므로 not exists에 의해 메인쿼리 결과가 모두 출력됨

3) 다중컬럼 서브쿼리
메인쿼리와 서브쿼리의 비교 컬럼이 둘 이상인 경우이다.
대소비교가 불가능하며, 연관서브쿼리 혹은 인라인뷰를 이용해야 한다.
예) emp 테이블을 사용하여 각 부서별 최대 급여 수령자의 이름, 부서번호, 급여를 출력하라.
select ename, deptno, sal
from emp
where (deptno, sal) in (select deptno, max(sal)
from emp
group by deptno);

예) student 테이블을 사용하여 각 학년별로 몸무게가 가장 많이 나가는 학생의 이름, 학년, 몸무게를 출력하라.
select name, grade, weight
from student
where (grade, weight) in (select grade, max (weight)
from student
group by grade)
order by 2;

-03 메인쿼리와의 독립성 여부에 따른 종류
1) 연관 서브쿼리
메인쿼리와 독립적으로 실행이 불가능한 서브퀘리의 형태를 뜻한다.
서브쿼리 내부에 메인쿼리 컬럼이 존재하는 형태이며, 메인쿼리의 각 행마다 서브쿼리가 매번 실행된다.
서브쿼리에서 메인쿼리를 계속해서 언급하고 있는 형태로 메인쿼리의 결과를 서브가 지속적으로 참조한다는 뜻이다.
select ename e1
from emp e1
where sal > (select avg(sal)
from emp e2
where e1.deptno = e2.deptno);

서브쿼리 내에서 메인쿼리를 언급하고 있으며, 동일한 테이블 내에서 행의 값과 평균을 비교해야하는 (가공해야하는) 상황이기에 연관 서브쿼리를 사용한다.
예) emp 테이블을 사용하여 각 부서별 평균 급여보다 높은 급여를 받는 직원의 이름, 부서번호, 급여 출력
select *
from emp e1
where sal > (select avg(sal)
from emp e2
where e2.deptno = e1.deptno);

예) student 테이블을 사용하여 각 학년별로 각 학년의 평균몸무게보다 적게 나가는 학생의 이름, 학년, 몸무게, 키 출력
select name, grade, weight, height
from student s1
where weight < (select avg(weight)
from student
where grade = s1.grade)
order by 2;

예제) professor 테이블을 사용하여 각 직급별로 평균급여보다 높은 급여를 받는 교수의 이름, 직급, 급여, 입사일 출력
select name, position, pay, hiredate
from professor p2
where pay > (select avg(pay)
from professor p1
where p1.position = p2.position)
order by 2,3;

다중행 서브쿼리와 join는 사용 목적에서 차이가 있다.
- join : 여러 테이블을 하나로 모아 사용해야 하며, 동시에 다른 테이블의 데이터를 한 번에 사용해야 할 때
-다중행 서브쿼리 : 필요한 열이 모두 한 테이블 안에 있으며, 동일한 테이블 안에서 추가해야하는 데이터의 가공이 필요할 때
* 연관 exist / not exists 쿼리
lecture 테이블은 2026년 강의를 배정받은 교수 및 강의에 대한 정보가 존재한다.
2026년에 강의를 배정받은 교수의 이름, 직급, 급여를 출력하라.
--2026년에 강의를 배정받은 교수의 이름, 직책, 급여...
select name, position, pay
from professor p
where exists (select *
from lecture l
where p.profno = l.profno);
select name, position, pay
from professor p
where profno in (select distinct profno
from lecture l);

--2026년에 강의를 배정받지 않은 교수의 이름, 직책, 급여... (not in과 같은 역할)
select name, position, pay
from professor p
where not exists (select *
from lecture l
where p.profno = l.profno);
select name, position, pay
from professor p
where profno not in (select distinct profno
from lecture l);

2) 비연관 서브쿼리
메인쿼리와 독립적으로 실행이 가능한 서브쿼리의 형태이다.
서브쿼리 내부에 메인쿼리 컬럼이 존재하지 않으며, 비연관 서브쿼리가 먼저 해석 -> 메인쿼리에 해당 결과를 상수의 형태로 전달한다.
따라서 단 한 번만 실행이 된다.
select ename
from emp
where sal > (select avg(sal) from emp);

서브쿼리가 메인쿼리와 독립적으로 실행 가능한 쿼리의 형태를 취하고 있으며, emp 테이블에서의 평균 급여를 구해 메인쿼리에 상수의 형태로 전달하고 있음을 확인할 수 있다.
'아이티윌_데이터 분석 55기 > 강의내용 필기_SQL' 카테고리의 다른 글
| #10 10일차_윈도우 함수 (0) | 2026.03.18 |
|---|---|
| #9 9일차_서브쿼리 : 인라인뷰, 스칼라 서브쿼리 (0) | 2026.03.17 |
| #7 7일차_JOIN : 조인 실습, 표준 조인 (0) | 2026.03.13 |
| #6 6일차_ JOIN : equi join, non equi join, 세 개 이상 테이블의 조인 (0) | 2026.03.12 |
| #5 5일차_날짜 파싱 시 유의사항, 변환 함수, NULL 치환 함수 (0) | 2026.03.11 |