집합 연산자는(SET OPERATOR)는 SELECT를 통해 얻은 결과 간의 집합 연산을 수행하는 연산자입니다. 2개 이상의 테이블에서 JOIN을 사용하지 않고 연관된 데이터를 조회하여 합쳐서 볼 수 있습니다. 즉, 2개 이상의 쿼리 수행 결과를 가지고 하나의 결과로 만들어줍니다. 동일 테이블에서 서로 다른 질의를 수행하여 결과를 합치고자 할 때 주로 사용할 수 있습니다.
집합 연산자를 사용하기 위해서는 SELECT 절의 칼럼 수가 동일해야 합니다. SELECT 절의 동일 위치에 존재하는 칼럼의 데이터 타입이 반드시 같을 필요는 없지만 암시적 데이터 형변환으로 상호 호환이 가능해야 합니다.
ORDER BY는 집합 연산을 적용한 최종 결과에 대한 정렬 처리이므로 가장 마지막 줄에 한 번만 기술하면 됩니다.
UNION, UNION ALL, INTERSECT(INTERSECTION), MINUS(EXCEPT)

개별 결과 집합이 있다면 UNION, UNION ALL, INTERSECT, MINUS (EXCEPT)를 이용하여 집합 연산을 할 수 있습니다. UNION ALL 을 제외한 나머지 집합 연산자들은 중복 데이터를 허용하지 않습니다.

집합 연산자 주의사항 정리
1. 두 집합의 칼럼 수가 일치해야 한다.
2. 두 집합의 칼럼 순서가 일치해야 한다.
3. 두 집합의 각 칼럼의 데이터 타입이 일치해야 한다.
4. 각 칼럼의 사이즈는 달라도 된다.
| Q. 문제 | 집합연산자 UNION과 UNION ALL에 대한 설명으로 옳지 않은 것은? |
|---|---|
| A. (1) | UNION은 중복된 행은 하나의 행으로 만든다 |
| A. (2) | UNION은 중복 배제하기 위한 정렬 연산이 있어 시스템의 부하가 있다 |
| A. (3) | UNION ALL은 중복된 행을 그대로 보여주며 중복된 행을 제외하면 UNION과 결과 집합 및 그 순서가 동일하다 |
| A. (4) | UNION ALL은 일반적으로 여러 개의 질의 결과가 중복이 없을 때(상호 배타적일 때) 사용한다 |
답
UNION과 UNION ALL의 결과 집합의 순서가 다를 수 있다
| Q. 문제 | 집합연산자에 대한 설명으로 가장 부적절한 것은? |
|---|---|
| A. (1) | UNION은 여러 개의 집합 연산에 대한 합집합을 출력한다 |
| A. (2) | UNION ALL은 여러 개의 집합 연산에 대한 합집합을 출력한다 |
| A. (3) | EXCEPT/MINUS는 앞의 SQL문의 결과에서 뒤의 SQL문의 결과에 차집합이다 |
| A. (4) | UNION과UNION ALL은 모두 중복을 허용한다 |
답
UNION은 중복을 허용하지 않는다
| Q. 문제 | UNION과 UNION ALL에 대한 설명으로 옳지 않은 것은? |
|---|---|
| A. (1) | UNION은 2개의 테이블에 대해 합집합을 만들 수 있다 |
| A. (2) | UNION은 중복을 제거한다 |
| A. (3) | UNION ALL은 정렬을 유발하지만 UNION은 정렬을 유발하지 않는다 |
| A. (4) | UNION과 UNION ALL을 사용할 때 2개 SELECT문에서 칼럼의 수가 일치해야 한다 |
답
UNION은 중복을 제거하기 때문에 정렬을 유발한다. 반면 UNION ALL은 중복을 제거하지 않기 때문에 정렬을 유발하지 않는다
서브쿼리(Subquery)란 하나의 SQL 문안에 포함되어 있는 또 다른 SQL 문을 의미합니다. 서브쿼리를 사용하면 SQL문을 통해 할 수 있는 일이 다양해지기 때문에 다양한 업무 처리가 가능하여 실무에서도 많이 사용합니다. 같이 상세하게 알아보도록 하겠습니다.
조인은 조인에 참여하는 모든 테이블에 대등한 관계에 있기 때문에 조인에 참여하는 모든 테이블의 칼럼을 어느 위치에서라도 자유롭게 참조가 가능합니다. 하지만 서브쿼리는 조인과 다르게 자유로운 형태의 참조가 아닌 특정 조건에 맞게 참조를 해야 합니다.
서브쿼리는 알려지지 않은 기준을 이용한 검색을 위해 사용되기 때문에 아래 그림과 같이 메인쿼리가 서브쿼리를 포함하는 형태를 띠게 됩니다. 즉, 서브쿼리는 레벨과는 상관없이 항상 메인쿼리 레벨로 결과 집합이 생성됩니다.
이러한 형태로 인해서 서브쿼리는 메인쿼리의 칼럼을 모두 사용할 수 있습니다. 그러나 반대의 경우 메인쿼리는 서브쿼리의 칼럼을 사용할 수가 없습니다. 메인쿼리는 서브쿼리에게 자신의 칼럼을 주는 것만 가능할 뿐 서브쿼리 내에 칼럼을 이용할 수가 없습니다.
포인트 정리
- 하나의 쿼리 안에 다른 작은 쿼리를 포함시키는 것
- 서브 쿼리는 주요 쿼리의 일부로 사용되어 조건을 만족시키거나 값을 가져올 때 유용
- 서브쿼리는 주인공 쿼리를 도와주는 작은 도우미 쿼리
- 데이터베이스에서 더 복잡한 정보를 가져오거나 원하는 조건을 충족시키기 위해 사용되는 도구
서브쿼리 사용 가능한 곳
- SELECT 절
- FROM 절
- WHERE 절
- HAVING 절
- ORDER BY 절
- DML(INSERT, DELETE, UPDATE절)
- GROUP BY 절에서 사용 불가
서브쿼리 사용 시 주의사항
1. 서브쿼리는 소괄호()로 감싸서 사용합니다.
2. 서브쿼리는 단일행 또는 복수행 비교 연산자와 함께 사용이 가능합니다.
- 단일 행 비교 연산자는 서브쿼리의 결과가 반드시 1건 이하여야 합니다.
- 복수 행 비교 연산자는 서브쿼리의 결과 건수와 관련이 없습니다. 1건도 가능하고 여러 건도 가능합니다.
3. 서브쿼리에서는 기본적으로 ORDER BY를 사용할 수 없습니다.
서브쿼리는 동작방식과 데이터 반환 형태를 가지고 분류할 수 있고, 이러한 분류 기준에 따라 비연관/연관서브쿼리, 단일행/다중행/다중칼럼 서브쿼리로 나뉘게 됩니다. 상세 내용을 살펴보겠습니다.

서브쿼리는 메인쿼리 안에 포함된 종속적인 관계이기 때문에 논리적인 실행순서는 항상 메인쿼리에서 읽힌 데이터에 대해 서브쿼리에서 해당 조건이 만족하는지 확인하는 방식으로 수행되어야 합니다.
연관 서브쿼리는 서브쿼리가 메인쿼리의 값을 사용하는 경우이며 비연관 서브쿼리는 서브쿼리가 메인쿼리의 값을 사용하지 않는 경우를 의미합니다.

비연관 서브쿼리 / 연관 서브쿼리
연관 서브쿼리는 서브쿼리 내에 메인쿼리 칼럼이 사용된 서브쿼리입니다. 즉, 서브쿼리의 값이 결정되는데 메인쿼리에 의존하는 것입니다
예시
각 부서별 평균 급여보다 급여 액수가 같거나 큰 직원들의 정보를 출력
SELECT A.EMPNO
, A.ENAME
, A.DEPTNO
, A.SAL
FROM EMP A
WHERE A.SAL >= (
SELECT AVG(X.SAL)
FROM EMP X
WHERE X.DEPTNO = A.DEPTNO
GROUP BY X.DEPTNO );

또한, EXISTS 서브쿼리는 항상 연관 서브쿼리로 사용되는데, EXISTS문은 서브쿼리의 결과가 참이라면 결과 집합에 포함시킵니다. EXISTS 서브쿼리는 아무리 조건을 만족하는 건이 여러 건이더라도 조건을 만족하는 1건만 찾으면 추가적인 검색을 진행하지 않습니다.
만약 서브쿼리가 메인 쿼리의 어떤 것도 참조하지 않고 단독으로 사용되면 비연관쿼리입니다. 서브 쿼리가 우선 실행되고, 그 결과가 메인쿼리의 WHERE 조건으로 이용되는 것입니다. 그러나 내부 쿼리는 외부 쿼리의 값과는 아무 상관이 없고, 단독으로도 쿼리가 실행될 수 있습니다.
서브쿼리는 특정한 값을 메인쿼리에 반환하는데, 이때 몇 개의 행 혹은 칼럼을 반환하는지에 따라서 단일행, 다중행, 그리고 다중칼럼 서브쿼리로 구분할 수 있습니다.

단일행 / 다중행 / 다중컬럼
단일행 서브쿼리는 서브쿼리의 결과가 0건 혹은 1건인 SQL 문이 서브쿼리로 존재하는 SQL문을 말합니다. 서브쿼리가 단일행 비교 연산자(=, <, <=, >, >=, <>)와 함께 사용할 때는 서브쿼리의 결과 건수가 반드시 1건 이하여야 합니다. 만약 2건 이상을 반환한다면 런타임 오류가 발생하게 됩니다.
예시
SELECT A.EMPNO, A.ENAME, A.SAL
FROM EMP A
WHERE A.SAL >= (SELECT AVG(K.SAL) AS MAX_SAL FROM EMP K)
ORDER BY A.SAL ;
다중행 서브쿼리는 서브 쿼리의 결과가 2건 이상인 SELECT문이 서브쿼리로 존재하는 것을 의미합니다.
서브쿼리의 결과가 2건 이상 반환될 수 있다면 반드시 다중행 비교 연산자(IN, ALL, ANY, EXISTS 등)와 함께 사용해야 합니다. 그렇지 않으면 SQL문은 오류를 반환하게 됩니다.
다중행 비교 연산자
IN (서브쿼리)
- 서브쿼리 결과에 존재하는 임의의 값과 동일한 조건을 의미합니다.
ALL (서브쿼리)- 서브쿼리 결과에 존재하는 모든 값을 만족하는 조건을 의미합니다.
ANY (서브쿼리) / SOME (서브쿼리)- 서브쿼리 결과에 존재하는 어느 하나의 값이라도 만족하는 조건을 의미합니다.
EXISTS (서브쿼리)- 서브쿼리 결과를 만족하는 값이 존재하는지 여부를 확인하는 조건을 의미합니다.
예시
--정상 실행
SELECT A.EMPNO, A.ENAME, A.DEPTNO
FROM EMP A
WHERE A.DEPTNO IN
(
SELECT K.DEPTNO
FROM DEPT K
WHERE K.DNAME IN ('ACCOUNTING', 'SALES')
)
ORDER BY A.DEPTNO ;
--error 발생
SELECT A.EMPNO, A.ENAME, A.DEPTNO
FROM EMP A
WHERE A.DEPTNO = (
SELECT K.DEPTNO
FROM DEPT K
WHERE K.DNAME IN ('ACCOUNTING', 'SALES')
)
ORDER BY A.DEPTNO ;
이때 주의할 점은 다중행 서브쿼리를 사용하면서 단일행 서브쿼리용 연산자 = 를 사용하면 안 된다는 것입니다.
다중칼럼 서브쿼리는 서브쿼리의 결과로 여러 개의 칼럼이 반환되어, 메인쿼리의 조건과 동시에 비교되는 것을 의미합니다. 예시를 통해서 알아보도록 하겠습니다.
예시 : 부서별로 가장 높은 급여를 받는 사원을 찾는 SQL 문
SELECT deptno, dname, empno, ename, sal
FROM (
SELECT e.deptno,
d.dname,
e.empno,
e.ename,
e.sal,
RANK() OVER (PARTITION BY e.deptno ORDER BY e.sal DESC) AS sal_rank
FROM emp e
JOIN dept d ON e.deptno = d.deptno
)
WHERE sal_rank = 1;
| Q. 문제 | <보기>의 설명은 어떤 서브 쿼리에 대한 설명인가? <보기> a. 실행 결과로 여러 칼럼을 반환하는 서브쿼리이다. b.서브 쿼리와 메인쿼리에서 비교하고자하는 칼럼 개수와 칼럼의 위치가 동일해야 한다. |
|---|---|
| A. (1) | 단일행 서브쿼리 |
| A. (2) | 다중행 서브쿼리 |
| A. (3) | 다중 칼럼 서브쿼리 |
| A. (4) | 연관 서브쿼리 |
답
다중칼럼 서브쿼리는 서브쿼리의 결과로 여러 개의 칼럼이 반환되어, 메인쿼리의 조건과 동시에 비교되는 것을 의미합니다.
| Q. 문제 | 다중행 비교 연산자의 종류로 적절하지 않은 것은? |
|---|---|
| A. (1) | ALL |
| A. (2) | ANY |
| A. (3) | EXISTS |
| A. (4) | INTEREST |
답
다중행 비교 연산자의 종류는 IN, ALL, ANY, SOME, EXISTS가 있다
| Q. 문제 | 서브쿼리에 대한 설명으로 옳은 것은? |
|---|---|
| A. (1) | 서브쿼리는 메인 쿼리의 결과와 독립적으로 실행된다 |
| A. (2) | EXISTS는 서브쿼리의 결괏값이 존재하는지를 확인하는 조건이다 |
| A. (3) | 서브쿼리는 항상 SELECT 문에서만 사용된다 |
| A. (4) | 서브쿼리는 항상 WHERE 절에서만 사용된다 |
답
2
서브쿼리는 위치를 기준으로도 구분할 수 있습니다.
select절에서 사용하는 서브쿼리를 '스칼라 서브쿼리'라고 합니다. 스칼라 서브쿼리의 가장 큰 특징은 하나의 행과 하나의 칼럼(1 Row - 1 Column)만을 반환하는 것입니다.
예시
SELECT A.EMPNO , A.ENAME, A.DEPTNO , (SELECT L.DNAME FROM DEPT L WHERE L.DEPTNO = A.DEPTNO ) AS DNAME FROM EMP A WHERE A.DEPTNO IN (SELECT K.DEPTNO FROM DEPT K WHERE K.DNAME IN ('ACCOUNTING', 'SALES')) ORDER BY A.DEPTNO;쿼리 결과는 하나의 행으로만 나와야합니다. 만약 두 개 이상의 복수 행이 나오게 되면 위 쿼리는 실행되지 않습니다.
SELECT ROWNUM, ENAME
FROM EMP
WHERE ROWNUM <=5;
SELECT ROWNUM, ENAME
FROM EMP
WHERE ROWNUM <= 5
ORDER BY ENAME;
SELECT ROWNUM, ENAME
FROM (SELECT ENAME FROM EMP ORDER BY ENAME)
WHERE ROWNUM <= 5;
FROM 절에서 사용하는 서브쿼리를 '인라인 뷰'라고 합니다. FROM절에는 테이블명이 반드시 오게 되어 있는데, FROM 절에 위치한 서브쿼리의 결과는 마치 실행 시에 동적으로 생성된 테이블인 것처럼 사용할 수 있습니다.
참조 - 뷰(View)
- 하나 이상의 테이블이나 다른 뷰의 데이터를 볼 수 있게 하는 데이터베이스 객체로 실제 데이터를 가지고 있지 않은 상태입니다.
- 반면에 테이블은 실제로 데이터를 가지고 있는 데이터베이스를 의미합니다.
- 상세한 내용은 이번 챕터 마지막에 다루게 됩니다.
인라인 뷰는 SQL 문이 실행될 때만 임시적으로 생성되는 뷰이기 때문에 데이터베이스에 해당 정보가 저장되지 않습니다. 그렇기 때문에 일반적인 뷰를 정적 뷰(Static View)라고 하고 인라인 뷰를 동적 뷰(Dynamic View)라고도 합니다
서브쿼리의 칼럼은 메인쿼리에서 사용할 수 없지만 인라인 뷰의 칼럼은 사용 가능한데, 이는 동적으로 생성된 테이블이기 때문입니다. 인라인 뷰를 사용하는 것은 조인 방식을 사용하는 것과 같기 때문에 칼럼을 자유롭게 참조할 수 있습니다.
예시
WHERE 절 안에 들어있는 서브쿼리입니다. 가장 많이 사용하며 '서브쿼리'라고 했을 때 가장 먼저 떠올리는 서브쿼리입니다. 여러 개의 서브쿼리가 겹쳐 있기 때문에 '중첩서브쿼리 (nested subqueries)' 라고도 부릅니다.
예시
SELECT empno, ename, deptno, sal
FROM emp
WHERE sal > (
SELECT AVG(sal)
FROM emp
);
실습
각 국가별로 평균 키를 계산하고, 이 평균 키보다 낮은 키를 가진 참가자의 정보를 출력해 보세요.
SELECT P.partic_id, P.first_name, P.last_name, P.nation_id, P.height
FROM PARTICIPANT P
WHERE P.height < (
SELECT AVG(PP.height)
FROM PARTICIPANT PP
WHERE P.nation_id = PP.nation_id
);
WHERE 대신 HAVING 절에서도 서브쿼리를 사용할 수 있습니다. HAVING절에서 서브쿼리는 그룹함수와 함께 사용될 때 그룹핑된 결과에 대해 부가적인 조건을 주기 위해서 사용합니다.
UPDATE시에도 서브쿼리를 사용하여 변경할 수 있습니다. 서브쿼리를 사용한 변경 작업을 할 때 주의할 점은 서브쿼리의 결과가 NULL 을 반환할 때입니다. WHERE 절은 UPDATE 대상이 되는 데이터의 범위를 결정하게 되는데, WHERE 절이 누락되면 모든 데이터가 UPDATE 대상이 되므로 NULL 값으로 변경될 수 있기 때문입니다. 서브쿼리를 활용하여, SELECT한 데이터를 바로 UPDATE할 수도 있습니다.
예시
UPDATE emp
SET sal = sal + 1000
WHERE deptno IN (
SELECT deptno
FROM dept
WHERE loc = 'DALLAS'
);
서브쿼리를 통해 새로운 값을 집어넣을 수도 있습니다
예시
INSERT_TEST 테이블을 생성 후 해당 테이블에 부서번호가 20인 직원의 가장 높은 급여를 삽입한다
CREATE TABLE INSERT_TEST (
DEPT_NO NUMBER
, MAX_SAL_AMT NUMBER(15) );
INSERT INTO INSERT_TEST
VALUES (20, (SELECT MAX(SAL)
FROM EMP
WHERE DEPTNO = 20)
);
서브쿼리는 여러 케이스를 함께 쓸 수도 있습니다. 스칼라 서브쿼리(SELECT절), 인라인뷰(FROM절), 서브쿼리(WHERE절)가 모두 쓰인 케이스를 통해 확인해 봅시다.
예시
SELECT A.계좌번호 , B.고객번호 , B.고객명 , C.적립금,
(SELECT 이름
FROM 사원 A
WHERE D.사원번호 = A.사원번호 ) 담당자_이름 -- 스칼라 서브쿼리
FROM 계좌 A
(SELECT 고객번호, MAX(적립금) 적립금
FROM 포인트
WHERE 적립일 = '20211030') C -- 인라인 뷰
WHERE NOT EXISTS (SELECT 'X'
FROM 입금목록 B
WHERE A.계좌번호 = B.계좌번호
AND B.거래일자 LIKE '202109%') -- 서브쿼리
AND A.지점_위치 = '서울'
AND A.고객번호 = C.고객번호
뷰는 하나 이상의 테이블이나 다른 뷰의 데이터를 볼 수 있게 하는 데이터베이스 객체입니다. 실제 데이터는 뷰를 구성하는 테이블에 담겨 있지만 마치 테이블처럼 사용할 수 있습니다. 다만, 테이블과는 조금 다른 특징이 있습니다. 테이블은 실제로 데이터를 가지고 있는 반면 뷰(View)는 실제 데이터를 가지고 있지 않습니다. 뷰는 단지 뷰 정의(View Definition)만을 가지고 있습니다. 질의에서 뷰가 사용되면 뷰 정의를 참조해서 DBMS 내부적으로 질의를 재작성(Rewrite)하여 수행하는 것입니다.

독립성 / 편리성 / 보안성
뷰는 실제 데이터를 가지고 있지 않지만 테이블이 수행하는 역할을 수행하기 때문에 '가상 테이블(Virtual Table)'이라고도 합니다. 뷰는 테이블뿐만 아니라 다른 뷰를 참조해 새로운 뷰를 만들어 사용할 수 있습니다. 데이터를 본다는 의미가 있으므로 뷰의 정의는 데이터를 조회하는 SELECT문으로 구성됩니다.
CREATE VIEW V_DEPT_EMP AS
SELECT E.EMPNO,
E.ENAME,
E.JOB,
E.SAL,
D.DNAME
FROM DEPT D,
EMP E
WHERE D.DEPTNO = E.DEPTNO;
참고하면 좋을 Tip
뷰 이름은 ‘V_’로 시작하는 게 좋습니다. 이러한 규칙을 사용하면 누구든 이 코드가 ‘뷰’라는 걸 알 수 있기 때문입니다.
위 예시에서는 뷰 이름을 ‘V_DEPT_EMP’라고 지정한 것을 확인할 수 있습니다.
예시
CREATE VIEW V_DEPT_EMP_FILTER AS
SELECT ENAME,
JOB
FROM V_DEPT_EMP
WHERE EMPNO IN (7698, 7788);
예시
SELECT ENAME,
JOB
FROM V_DEPT_EMP
WHERE EMPNO IN (7698, 7788);
예시
뷰를 제거하기 위해서는 DROP VIEW 문을 사용
DROP VIEW V_DEPT_EMP;
DROP VIEW V_DEPT_EMP_FILTER;
실습
참가자와 국가 테이블을 JOIN 한 v_p_nation VIEW 를 생성하세요
CREATE VIEW v_p_nation AS
SELECT p.first_name,
p.last_name,
p.main_sport_id,
p.height,
p.weight,
n.country_name,
n.population
FROM participant p, nation n
WHERE p.nation_id = n.nation_id;
| Q. 문제 | 아래 <보기> 에서 ㉠ 에 들어갈 알맞은 단어는? <보기> 1. 뷰는 실제 데이터를 가지고 있지 않지만 테이블이 수행하는 역할을 수행한다. 2. 뷰는 ㉠ VIEW AS 문으로 정의가 가능하다 |
|---|---|
| A. (1) | VIRTUAL |
| A. (2) | FROM |
| A. (3) | SELECT |
| A. (4) | CREATE |
답
뷰는 CREATE VIEW AS문으로 생성한다
| Q. 문제 | 뷰의 특징과 설명으로 잘못된 것은? |
|---|---|
| A. (1) | 독립성 : 테이블 구조가 변경되어도 뷰를 사용하는 응용프로그램은 변경하지 않아도 된다 |
| A. (2) | 편리성 : 복잡한 질의를 뷰로 생성함으로써 관련 질의를 단순하게 작성할 수 있다 |
| A. (3) | 보안성 : 직원들의 급여 정보와 같은 숨겨야하는 정보가 있다면 해당 칼럼을 빼고 뷰 생성이 가능하다 |
| A. (4) | 물리성 : DBMS 내부의 객체로 존재하여 테이블 내에 존재하는 데이터를 물리적으로 관리할 수 있다 |
답
뷰는 실제 테이블이 아니며 DBMS내부의 객체로 존재하지 않는다
| Q. 문제 | 서브쿼리 중에서 반드시 한 행과 한 칼럼만 반환하는 것은? |
|---|---|
| A. (1) | Inline view |
| A. (2) | Subquery |
| A. (3) | Scala Subquery |
| A. (4) | Update Subquery |
답
스칼라 서브쿼리