SELECT JOB FROM EMP WHERE DEPTNO = 10 INTERSECT SELECT JOB FROM EMP WHERE DEPTNO = 20 ORDER BY JOB;
JOB---------CLERKMANAGER
SELECT JOB FROM EMP WHERE DEPTNO = 10 MINUS SELECT JOB FROM EMP WHERE DEPTNO = 20 ORDER BY JOB;
JOB---------PRESIDENT
SELECT JOB FROM EMP WHERE DEPTNO = 20 MINUS SELECT JOB FROM EMP WHERE DEPTNO = 10 ORDER BY JOB;
JOB---------ANALYST
INTERSECT 는 양쪽 모두에 있는 직무만 남긴다. 순서를 바꿔도 결과가 같다.
MINUS 는 앞 결과에서 뒤 결과에 있는 것을 뺀다. 순서를 바꾸면 결과가 달라진다. 10 − 20 은 PRESIDENT, 20 − 10 은 ANALYST 다.
MINUS 로 "사원이 없는 부서"를 구한다. 어제의 NOT EXISTS와 같은 질문을 집합으로 푼 것이다.
SELECT DEPTNO FROM DEPT MINUS SELECT DEPTNO FROM EMP;
DEPTNO---------- 40
DBMS
차집합 키워드
Oracle
MINUS
SQL Server
EXCEPT
SQLD 는 Oracle 은 MINUS, SQL Server 는 EXCEPT 로 구분한다. 이 노트의 실습 컨테이너(Oracle Database 23ai)는 21c 부터 들어온 EXCEPT도 받아 MINUS 와 같은 결과를 낸다. 시험에서는 Oracle 차집합을 MINUS 로 본다.
SELECT EMPNO AS ID, ENAME AS NAME FROM EMP WHERE DEPTNO = 10 UNION SELECT DEPTNO, DNAME FROM DEPT ORDER BY ID;
ID NAME---------- -------------- 10 ACCOUNTING 20 RESEARCH 30 SALES 40 OPERATIONS 7782 CLARK 7839 KING 7934 MILLER
열 제목은 첫 SELECT 의 별칭 ID·NAME 이다. 두 번째 SELECT 의 DEPTNO·DNAME 은 쓰이지 않는다.
NAME 열 폭이 14 로 넓어진 것은 DNAME(14자)에 맞춘 것이다. 이름은 첫 SELECT 를 따르고, 자료형 길이는 양쪽을 담을 수 있게 정해진다.
ORDER BY 도 첫 SELECT 의 이름으로만 가리킨다. 두 번째 SELECT 의 별칭이나 열 이름을 쓰면 오류다.
SELECT ENAME FROM EMP WHERE DEPTNO = 10 UNION SELECT DNAME FROM DEPT WHERE DEPTNO = 10 ORDER BY DNAME;
SELECT ENAME FROM EMP WHERE DEPTNO = 10 UNION SELECT DNAME FROM DEPT WHERE DEPTNO = 10 ORDER BY DNAME *ERROR at line 1:ORA-00904: "DNAME": invalid identifier
SELECT ENAME FROM EMP WHERE DEPTNO = 10 UNION SELECT DNAME FROM DEPT WHERE DEPTNO = 10 ORDER BY ENAME;
ORDER BY 는 집합 연산이 끝난 최종 결과를 정렬한다. 중간 SELECT 뒤에 쓰면 문법 오류다.
SELECT JOB FROM EMP WHERE DEPTNO = 10 ORDER BY JOB UNION SELECT JOB FROM EMP WHERE DEPTNO = 20;
SELECT JOB FROM EMP WHERE DEPTNO = 10 ORDER BY JOB UNION SELECT JOB FROM EMP WHERE DEPTNO = 20 *ERROR at line 1:ORA-03048: SQL reserved word 'UNION' is not syntactically valid following '...DEPTNO = 10 ORDER BY JOB '
실습 컨테이너(23ai)는 ORA-03048 로 어느 단어가 문제인지 알려 준다. 이전 버전 Oracle 은 같은 문장에 ORA-00933(SQL command not properly ended)을 낸다. 어느 쪽이든 오류다.
⚠️ 함정: 집합 연산 문장의 ORDER BY 는 마지막 SELECT 뒤에 한 번만 쓴다. 정렬 기준은 첫 SELECT 의 열 이름·별칭이나 위치 번호다. 마지막 SELECT 에만 있는 이름은 쓸 수 없다.
집합 연산자 : 두 SELECT 결과를 행 단위로 합치거나 빼거나 겹치는 부분을 고르는 연산자.
UNION / UNION ALL : 합집합. UNION 은 중복을 없애고, UNION ALL 은 중복을 그대로 둔다.
INTERSECT : 양쪽 결과에 모두 있는 행만 남기는 교집합.
MINUS / EXCEPT : 앞 결과에서 뒤 결과에 있는 행을 뺀 차집합. Oracle 은 MINUS, SQL Server 는 EXCEPT 다.
ORA-01789 : 집합 연산의 위아래 SELECT 열 개수가 다를 때의 오류.
ORA-01790 : 같은 위치 열의 자료형이 서로 맞지 않을 때의 오류.
중복 제거 비용 : UNION·INTERSECT·MINUS 가 같은 행을 찾으려고 하는 정렬·해시 작업. UNION ALL 에는 없다.
📝 연습 문제
선택지를 클릭하면 정답·해설이 펼쳐집니다.
문제 1
EMP 의 10번 부서는 3명, 20번 부서는 5명이며, 두 부서에 공통으로 있는 직무는 CLERK·MANAGER 다. 다음 SQL 의 결과는?
```sql
SELECT COUNT(*) FROM (SELECT JOB FROM EMP WHERE DEPTNO = 10 UNION ALL SELECT JOB FROM EMP WHERE DEPTNO = 20);
```
문제 2
다음 SQL 을 Oracle 에서 실행한 결과로 알맞은 것은?
```sql
SELECT ENAME FROM EMP WHERE DEPTNO = 10 UNION SELECT DNAME FROM DEPT WHERE DEPTNO = 10 ORDER BY DNAME;
```
문제 3
DEPT 에는 10·20·30·40번 부서가 있고, EMP 에는 10·20·30번 부서 사원만 있다. 사원이 없는 부서 번호 40 만 구하려 할 때 빈칸에 들어갈 연산자는?
```sql
SELECT DEPTNO FROM DEPT ( 빈칸 ) SELECT DEPTNO FROM EMP;
```
문제 4
집합 연산자에 대한 설명으로 옳지 않은 것은?
문제 5
10번 부서 직무는 CLERK·MANAGER·PRESIDENT, 20번은 ANALYST·CLERK·MANAGER, 30번은 CLERK·MANAGER·SALESMAN 이다. 다음 SQL 의 결과는?
```sql
SELECT JOB FROM EMP WHERE DEPTNO = 10 UNION SELECT JOB FROM EMP WHERE DEPTNO = 20 MINUS SELECT JOB FROM EMP WHERE DEPTNO = 30 ORDER BY JOB;
```