1. 서브쿼리 개요
1️⃣ 서브쿼리의 기초 개념
1) 쿼리(Query)
- 하나의 명령 문장
- 메인쿼리(Mainquery)라고 함
2) 서브쿼리(Subquery)
- 메인쿼리 안에서 또 다른 명령 문장이 독립정으로 실행되는 것
- 내부쿼리 또는 중첩쿼리라고도 함
- 복잡한 쿼리를 단순화하고, 특정 조건에 맞는 데이터를 필터링하거나 계산할 때 사용
- 메인쿼리의 일부로 실행되며, 메인쿼리와 구분하기 위해 서브쿼리는 괄호로 감싸서 사용
- 메인쿼리의 WHERE, FROM, SELECT절 등에 사용됨
3) 형식
- 메인쿼리
SELECT 칼럼1, 칼럼2, ...
FROM 테이블
WHERE 칼럼1 = 칼럼2;- 메인쿼리 내의 서브쿼리
SELECT 칼럼1, 칼럼2, ...
FROM 테이블
WHERE 칼럼1 = (
SELECT 칼럼
FROM 테이블
WHERE 조건 /* 세미콜론(;) 사용하지 않음 */
);2️⃣ 언제 서브쿼리를 사용하는가?
- WHERE절에서 조건을 추가하고 싶을 때
- 데이터를 좀 더 복잡한 조건을 만들어 필터링하고 싶을 때
- 원래 데이터에는 존재하지 않는 계산된 칼럼을 생성하고 싶을 때 SELECT 문에서 사용
- DB에 테이블을 만들지 않고, 쿼리 안에서만 임시로 FROM절에 테이블을 만들어 사용(IN-LINE-VIEW)
- 일부 작업은 서브쿼리가 조인보다 간단함
특히, EXISTS 또는 IN 처럼 존재 여부를 확인하는 경우, 서브쿼리가 적합
3️⃣ 서브쿼리의 종류
1) 동작하는 방식에 따른 서브쿼리
연관(Correlated) 서브쿼리 | • 메인쿼리의 칼럼은 참조하는 서브쿼리
• 메인쿼리의 각 행에 대해 서브쿼리가 실행됨
• 메인쿼리의 칼럼 값을 이용해 서브쿼리의 결과를 계산함
• 메인쿼리의 각 행마다 실행되므로 처리 시간이 더 소요됨 |
비 연관(Un-Correlated) 서브쿼리 | • 메인쿼리와 독립적으로 실행되는 서브쿼리
• 메인쿼리의 칼럼을 참조하지 않음
• 서브쿼리가 먼저 실행되고, 그 결과가 메인쿼리에서 사용됨
• 보통 한 번만 실행됨 |
2) 반환되는 데이터의 형태에 따른 서브쿼리
단일행(Single Row) 서브쿼리 | • 하나의 행만 반환하는 서브쿼리
(0행일 경우, 서브쿼리의 반환값을 NULL로 간주)
• 단일 값을 반환
• 주로 비교 연산자( =, >, <, >=, <=, <>)와 함께 사용 |
다중행(Multi Row) 서브쿼리 | • 여러 행을 반환하는 서브쿼리
• 하나 이상의 행을 반환
• IN, ANY, ALL, EXISTS 같은 연산자와 함께 사용 |
다중칼럼(Multi Column) 서브쿼리 | • 여러 칼럼을 반환하는 서브쿼리
• 두 개 이상의 칼럼을 반환
• 주로 복합 비교에 사용 |
2. 서브쿼리의 종류
1️⃣ 단일행 서브쿼리
0) 단일행 서브쿼리
- 오직 하나의 행(row)만을 반환하는 서브쿼리
(0행일 경우, 서브쿼리의 반환값을 NULL로 간주)
- 단일행 서브쿼리는 하나의 행을 반환하며, 메인쿼리에서 하나의 값처럼 사용됨
- 주로 비교 연산자(
=,>,<,>=,<=,<>) 및 집계 함수(AVG,SUM,MAX,MIN,COUNT등)와 함께 자주 사용됨
1) WHERE 절에서의 단일행 서브쿼리 사용
- 급여가 평균 급여보다 높은 사원 정보 조회
SELECT ENAME, SAL
FROM EMP
WHERE SAL > (
SELECT AVG(SAL)
FROM EMP
)
ORDER BY SAL DESC;ENAME | SAL |
KING | 5000 |
FORD | 3000 |
SCOTT | 3000 |
JONES | 2975 |
BLAKE | 2850 |
CLARK | 2450 |
➡️ EMP 테이블의 평균 급여(2073.21)보다 높은 사원 조회
2) HAVING 절에서의 단일행 서브쿼리 사용
- 부서의 평균 급여가 전체 평균 급여보다 높은 부서 조회
SELECT DEPTNO, AVG(SAL)
FROM EMP
GROUP BY DEPTNO
HAVING AVG(SAL) > (
SELECT AVG(SAL)
FROM EMP);DEPTNO | AVG(SAL) |
10 | 2916.667 |
➡️ EMP 테이블의 평균 급여(2073.21)보다 높은 부서 조회
3) SELECT 절에서의 단일행 서브쿼리 사용
- 각 행에서 전체 평균 급여를 함께 표시
SELECT ENAME, SAL, (
SELECT AVG(SAL)
FROM EMP
) AS AVG_SAL
FROM EMP
ORDER BY SAL DESC;ENAME | SAL | AVG_SAL |
KING | 5000 | 2073.214 |
FORD | 3000 | 2073.214 |
SCOTT | 3000 | 2073.214 |
… | … | … |
4) FROM 절에서의 단일행 서브쿼리 사용 (IN-LINE-VIEW)
- 평균 급여를 FROM 절에서 조회하고 메인쿼리에서 평균 급여보다 높은 급여를 받는 사원 조회
SELECT E.ENAME, E.SAL, A.AVG_SAL
FROM EMP E, (
SELECT AVG(SAL) AS AVG_SAL
FROM EMP
) A
WHERE E.SAL > A.AVG_SAL
ORDER BY SAL DESC;ENAME | SAL | AVG_SAL |
KING | 5000 | 2073.214 |
FORD | 3000 | 2073.214 |
SCOTT | 3000 | 2073.214 |
JONES | 2975 | 2073.214 |
BLAKE | 2850 | 2073.214 |
CLARK | 2450 | 2073.214 |
5) INSERT 문에서의 단일행 서브쿼리 사용
- 새 사원을 추가할 때 자동으로 다음 사원 번호를 생성
INSERT INTO EMP (EMPNO, ENAME, SAL) VALUES ((
SELECT MAX(EMPNO) + 1
FROM EMP
),
'NEW_EMP', 3000);EMPNO | ENAME | SAL | ㅤ | EMPNO | ENAME | SAL |
7934 | MILLER | 1300 | ㅤ | 7935 | NEW_EMP | 3000 |
7902 | FORD | 3000 | -————> | 7934 | MILLER | 1300 |
7900 | JAMES | 1264.45 | 쿼리 수행 후 | 7902 | FORD | 3000 |
7876 | ADAMS | 1100 | ㅤ | 7900 | JAMES | 1264.45 |
7844 | TURNER | 1996.5 | ㅤ | 7876 | ADAMS | 1100 |
… | … | … | ㅤ | … | … | … |
6) UPDATE 문에서의 사용
- SALES 부서의 모든 사원의 급여를 10% 인상하고 NEW_SAL에 값 입력
-- 신규 칼럼을 추가하는 SQL
ALTER TABLE EMP
ADD NEW_SAL NUMBER(7,2);
-- SALES 부서의 모든 사원의 급여를 10% 인상하고 NEW_SAL에 값 입력
-- EMP 테이블의 DEPTNO=30으로 해도 되지만, 여기서는 업데이트 문제에서의 서브쿼리를 살펴보기
-- 위한 예제이므로 WHERE 절에 서브쿼리를 사용함
UPDATE EMP
SET NEW_SAL = SAL * 1.1
WHERE DEPT = (
SELECT DEPTNO
FROM DEPT
WHERE DNAME = 'SALES'
);
-- 업데이트 후 조회
SELECT E.EMPNO, E.ENAME, E.DEPTNO, D.DNAME, E.SAL, E.NEW_SAL
FROM EMP E JOIN DEPT D
ON E.DEPTNO = D.DEPTNO
WHERE D.DNAME = 'SALES';EMPNO | ENAME | DEPTNO | DNAME | SAL | NEW_SAL |
7521 | WARD | 30 | SALES | 1250 | 1375 |
7499 | ALLEN | 30 | SALES | 1600 | 1760 |
7844 | TURNER | 30 | SALES | 1500 | 1650 |
… | … | … | … | … | … |
7) DELETE 문에서의 사용
- 평균 급여 미만을 받는 모든 사원 삭제
[쿼리 수행 전] - EMP 테이블에 AVG 칼럼 추가해서 평균 미만의 급여 받는 사원 확인
SELECT ENAME, SAL, (
SELECT AVG(SAL)
FROM EMP
) AVG
FROM EMP
ORDER BY DESC;ENAME | SAL | AVG |
MILLER | 1300 | 2073.214 |
FORD | 3000 | 2073.214 |
JAMES | 950 | 2073.214 |
ADAMS | 1100 | 2073.214 |
TURNER | 1500 | 2073.214 |
KING | 5000 | 2073.214 |
SCOTT | 3000 | 2073.214 |
… | … | … |
DELETE
FROM EMP
WHERE SAL < (
SELECT AVG(SAL)
FROM EMP);[쿼리 수행 후]
ENAME | SAL | AVG |
FORD | 3000 | 2073.214 |
KING | 5000 | 2073.214 |
SCOTT | 3000 | 2073.214 |
CLARK | 2450 | 2073.214 |
BLAKE | 2850 | 2073.214 |
JONES | 2975 | 2073.214 |
8) 스칼라 서브쿼리 (칼럼 대신 값을 반환)
- 각 사원의 부서 이름을 함께 조회
SELECT ENAME, SAL, (
SELECT DNAME
FROM DEPT
WHERE DEPT.DEPTNO = EMP.DEPTNO
) AS DEPT_NAME
FROM EMP
ORDER BY EMPNO DESC;ENAME | SAL | DEPT_NAME |
MILLER | 1300 | ACCOUNTING |
FORD | 3000 | RESEARCH |
JAMES | 950 | SALES |
… | … | … |
9) 상관 서브쿼리
- 메인 쿼리의 각 행에 의존하여 실행되는 형태의 서브쿼리
- 각 사원의 급여가 자신이 속한 부서의 평균 급여보다 높은 경우를 조회
SELECT ENAME, DEPTNO, SAL
FROM EMP E1
WHERE SAL > (
SELECT AVG(SAL)
FROM EMP E2
WHERE E1.DEPTNO = E2.DEPTNO
)
ORDER BY EMPNO DESC;ENAME | DEPTNO | SAL |
FORD | 30 | 3000 |
KING | 10 | 5000 |
SCOTT | 20 | 3000 |
BLAKE | 30 | 2850 |
JONES | 20 | 2975 |
10) CASE 문에서의 사용
- 각 사원의 급여가 평균 이상인지 미만인지 표시
SELECT ENAME, SAL, CASE
WHEN SAL > (
SELECT AVG(SAL)
FROM EMP)
THEN 'Above Average'
ELSE 'Below Average'
END AS SALARY_STATUS
FROM EMP;ENAME | SAL | SALARY_STATUS |
MILLER | 1300 | Below Average |
FORD | 3000 | Above Average |
JAMES | 950 | Below Average |
… | … | … |
2️⃣ 다중행 서브쿼리
0) 다중행 서브쿼리
- 하나 이상의 행을 반환하는 서브쿼리
- 다중행 연산자(IN, ANY, ALL, EXISTS 등)와 함께 사용됨
- 하나 이상의 행을 반환하므로 단일행 연산자(
=,>,<,>=,<=,<>) 를 직접 사용할 수 없음
1) IN
- 서브쿼리가 반환한 여러 행 중 하나라도 일치하면 조건을 만족
- 부서 위치가 뉴욕에 있는 사원의 정보 조회
SELECT ENAME, DEPTNO
FROM EMP
WHERE DEPTNO IN (
SELECT DEPTNO
FROM DEPT
WHERE LOC = 'NEW YORK'
);[쿼리 수행 전] - EMP 테이블과 DEPT 테이블을 DEPTNO로 조인하여 DNAME과 LOC 조회
SELECT A.ENAME, A.DEPTNO, B.DNAME, B.LOC
FROM EMP A, DEPT B
WHERE A.DEPTNO = B.DEPTNO;ENAME | DEPTNO | DNAME | LOC |
KING | 10 | ACCOUNTING | NEW YORK |
BLAKE | 30 | SALES | CHICAGO |
CLARK | 10 | ACCOUNTING | NEW YORK |
… | … | … | … |
[쿼리 수행 후]
ENAME | DEPTNO |
KING | 10 |
CLARK | 10 |
MILLER | 10 |
2) ANY 또는 SOME
- 서브쿼리가 반환한 여러 행 중 하나라도 일치하면 조건을 만족
- 10번 부서에 속한 어떤 직원의 급여보다 더 높은 급여를 받는 사원 조회
SELECT ENAME, SAL
FROM EMP
WHERE SAL > ANY (
SELECT SAL
FROM EMP
WHERE DEPTNO = 10
);[쿼리 수행 전]
ENAME | DEPTNO | DNAME | SAL |
KING | 10 | ACCOUNTING | 5000 |
CLARK | 10 | ACCOUNTING | 2450 |
MILLER | 10 | ACCOUNTING | 1300 |
… | … | … | … |
[쿼리 수행 후]
ENAME | SAL |
KING | 5000 |
SCOTT | 3000 |
FORD | 3000 |
JONES | 2975 |
BLAKE | 2850 |
CLARK | 2450 |
ALLEN | 1600 |
TURNER | 1500 |
3) ALL
- 서브쿼리가 반환한 모든 행이 조건을 만족
- 20번 부서에 속한 모든 직원의 급여보다 더 높은 급여를 받는 사원 조회
SELECT ENAME, SAL
FROM EMP
WHERE SAL > ALL (
SELECT SAL
FROM EMP
WHERE DEPTNO = 20
);[쿼리 수행 전]
ENAME | DEPTNO | DNAME | SAL |
JONES | 20 | RESEARCH | 2975 |
FORD | 20 | RESEARCH | 3000 |
SMITH | 20 | RESEARCH | 800 |
SCOTT | 20 | RESEARCH | 3000 |
… | … | … | … |
[쿼리 수행 후]
ENAME | SAL |
KING | 5000 |
4) EXISTS
- 서브쿼리가 한 행이라도 반환하면 조건을 만족
- 두 테이블에서 동일한 부서이면서 부서의 위치가 ‘DALLAS’인 사원명과 부서번호 조회
SELECT ENAME, DEPTNO
FROM EMP E
WHERE EXISTS (
SELECT 1
FROM DEPT D
WHERE D.DEPTNO = E.DEPTNO
AND D.LOC = 'DALLAS'
);[쿼리 수행 전]
ENAME | DEPTNO | DNAME | LOC |
KING | 10 | ACCOUNTING | NEW YORK |
BLAKE | 30 | SALES | CHICAGO |
CLARK | 10 | ACCOUNTING | NEW YORK |
JONES | 20 | RESEARCH | DALLAS |
… | … | … | … |
[쿼리 수행 후]
ENAME | DEPTNO |
JONES | 20 |
FORD | 20 |
SMITH | 20 |
SCOTT | 20 |
ADAMS | 20 |
3️⃣ 다중칼럼 서브쿼리
0) 다중칼럼 서브쿼리(Multi-Column Subquery)
- 서브쿼리에서 여러 칼럼을 반환하여 메인쿼리의 조건과 비교하는 서브쿼리
- 일반적으로 두 개 이상의 칼럼을 비교할 때 사용되며, 비교 연산자로 IN, EXISTS 등을 사용
1) IN 연산자와 다중칼럼 서브쿼리
- SAL이 3000보다 큰 직원들의 JOB과 DEPTNO를 서브쿼리로 찾고, 그 JOB과 DEPTNO를 가진 EMP 테이블의 직원들 조회
SELECT ENAME, JOB, DEPTNO
FROM EMP
WHERE (JOB, DEPTNO) IN (
SELECT JOB, DEPTNO
FROM EMP
WHERE SAL > 3000
);ENAME | JOB | DEPTNO |
KING | PRESIDENT | 10 |
2) EXISTS 연산자와 다중칼럼 서브쿼리
- 서브쿼리가 한 행이라도 반환하면 조건을 만족
- ‘DALLAS’에 위치한 부서에 속한 모든 직원의 이름과 사원 번호 조회
SELECT ENAME, DEPTNO
FROM EMP E
WHERE EXISTS (
SELECT 1
FROM DEPT D
WHERE D.DEPTNO = E.DEPTNO
AND D.LOC = 'DALLAS'
);ENAME | DEPTNO |
JONES | 20 |
FORD | 20 |
SMITH | 20 |
SCOTT | 20 |
ADAMS | 20 |
4️⃣ 연관 서브쿼리
0) 연관 서브쿼리 (Correlated Subquery)
- 메인쿼리의 각 행에 대해 서브쿼리가 한 번씩 실행되는 서브쿼리
- 서브쿼리는 메인쿼리의 각 행과 관련되어 있으며, 메인쿼리의 데이터를 참조
- 연관 서브쿼리는 반복적으로 실행되므로 일반 서브쿼리보다 성능 떨어짐
- 서브쿼리는 메인쿼리의 행 데이터를 참조하여 각 행에 대해 조건을 평가
- 서버쿼리는 메인쿼리와 관련된 데이터를 기반으로 동적으로 실행
1) 연관 서브쿼리 #1
- 각 직원의 급여가 동일한 부서의 평균 급여보다 높은 직원을 조회
SELECT ENAME, SAL, DEPTNO
FROM EMP E1
WHERE SAL > (
SELECT AVG(SAL)
FROM EMP E2
WHERE E1.DEPTNO = E2.DEPTNO
);ENAME | SAL | DEPTNO |
KING | 5000 | 10 |
BLAKE | 2850 | 30 |
JONES | 2975 | 20 |
ALLEN | 1600 | 30 |
FORD | 3000 | 20 |
SCOTT | 3000 | 20 |
2) 연관 서브쿼리 #2
- 각 부서에서 가장 최근에 고용된 직원 조회
SELECT ENAME, HIREDATE, DEPTNO
FROM EMP E1
WHERE HIREDATE = (
SELECT MAX(HIREDATE)
FROM EMP E2
WHERE E1.DEPTNO = E2.DEPTNO
);ENAME | HIREDATE | DEPTNO |
JAMES | 1981-12-11 | 30 |
ADAMS | 1983-01-15 | 20 |
MILLER | 1982-01-11 | 10 |
5️⃣ 뷰 (VIEW)
0) 뷰 (VIEW)
- 하나 이상의 테이블에서 데이터를 검색하는 SQL 쿼리의 결과를 저장하는 가상 테이블
- 실제 데이터를 저장하지 않고, 기본 테이블에서 데이터를 가져와 동적이며 읽기 전용의 데이터 집합 제공
- 특징
- 복잡한 쿼리를 단순화
- 데이터 접근을 제어
- 보안과 편의성 높일 수 있음
1) 뷰(View) 형식
- SELECT문을 기본으로 함
- 가장 첫 줄에 CREATE VIEW 적고 뷰 이름을 지정
CREATE VIEW 뷰이름
SELECT 칼럼1, 칼럼2, ...
FROM 테이블
WHERE 조건;2) 뷰(View)의 예
- 부서의 지역(LOC)이 ‘CHICAGO’인 부서의 직원 정보를 포함하는 뷰 생성
CREATE VIEW EMP_DEPT_VIEW AS
SELECT E.ENAME, E.JOB, D.DNAME, D.LOC
FROM EMP E JOIN DEPT D
ON E.DEPTNO = D.DEPTNO
WHERE D.LOC = 'CHICAGO';3) 뷰의 장점
- 데이터 중복 방지
- 뷰는 실제 데이터를 저장하지 않아 중복 피할 수 있음
- 데이터 일관성
- 뷰는 항상 최신 데이터를 보여줌
- 보안 강화
- 뷰를 통해 특정 칼럼이나 행에 대한 접근 제한할 수 있음
- 원본 테이블의 구조를 숨기면서 필요한 정보만 제공
- 복잡한 쿼리 단순화
- 자주 사용되는 복잡한 쿼리를 뷰로 만들어 재사용
- 코드의 가독성과 유지보수성 높임
- 실무에서 뷰를 사용하는 가장 큰 이유 중 하나
- 유연성
- 기본 테이블 구조가 변경되어도 뷰를 통해 일관된 인터페이스 제공
- 애플리케이션 코드 수정 없이 뷰만 수정하면 됨
- 성능 최적화
- 일부 DBMS에서는 materialized view를 제공하여 성능 향상
- 논리적 데이터 독립성
- 논리적 데이터 독립성
- 데이터베이스의 논리적 구조(테이블 , 칼럼 등)와 물리적 구조(저장 경로, 저장 파일, 색인 등)를 분리하여, 한쪽의 변화가 다른 쪽에 영향을 미치지 않도록 하는 데이터베이스의 설계 원칙
- 뷰는 논리적 데이터 독립성을 실현하는 중요한 수단
- 뷰를 통해 물리적 데이터 구조와 논리적 데이터 구조 분리할 수 있음
- 저장 공간 절약
- 실제 데이터 저장하지 않으므로 추가 저장 공간 필요 없음
- 시스템 카탈로그 용이
- 시스템 카탈로그
- 데이터베이스에서 테이블, 뷰, 색인, 제약 조건, 사용자 권한 등 데이터베이스에 대한 메타데이터를 관리하는 특수한 데이터베이스 영역
- 뷰는 논리적 데이터 구조로서 가상 테이블이며, 물리적으로 데이터를 저장하지 않기 때문에 시스템 카탈로그에 영향을 덜 미침
4) 뷰(View)와 테이블(Table)의 주요 차이점
특성 | 테이블 (Table) | 뷰 (View) |
데이터 저장 | 실제 데이터를 물리적으로 저장 | 데이터를 저장하지 않음
(쿼리 정의만 저장) |
생성 방식 | CREATE TABLE 문으로 생성 | CREATE VIEW 문으로 생성
(SELECT 문 기반) |
데이터 수정 | 직접적인 INSERT, UPDATE, DELETE 가능 | 일반적으로 읽기 전용
(특정 조건에서 수정 가능) |
용도 | 데이터의 기본 저장소 | 데이터 접근 제어, 쿼리 단순화, 데이터 요약 |
성능 | 직접 접근으로 일반적으로 빠름 | 쿼리 실행 시 계산되어 약간의 오버헤드 가능 |
독립성 | 독립적으로 존재 | 하나 이상의 테이블에 종속적 |
인덱스 | 인덱스 생성 가능 | 직접적인 인덱스 생성 불가
(일부 DBMS 예외) |
저장 공간 | 실제 데이터만큼의 공간 필요 | 쿼리 정의만 저장하여 최소한의 공간 필요 |
데이터 중복 | 데이터 중복 가능성 있음 | 데이터 중복 없음 |
데이터 일관성 | 직접 관리 필요 | 항상 최신 데이터 반영 |
보안 | 테이블 수준의 보안 | 칼럼 또는 행 수준의 세밀한 보안 가능 |
Share article