집합 연산자는 두 SELECT 문의 결과를 집합처럼 다뤄 하나의 결과로 만듭니다. 합치는 UNION·UNION ALL, 겹치는 행만 남기는 INTERSECT, 빼는 EXCEPT가 있습니다.
자료구조(DS)를 들은 학번과 네트워크(NW)를 들은 학번을 따로 구했다고 해 봅시다. '둘 가운데 하나라도 들은 학생', '둘 다 들은 학생', '자료구조만 들은 학생'은 두 결과를 견줘야 답할 수 있습니다. 집합 연산자는 두 SELECT 문 사이에 연산자 하나를 넣어 이런 물음에 답합니다. 관계대수의 합집합(∪)·교집합(∩)·차집합(−)을 SQL로 쓰는 방법입니다. 시험에는 연산마다 결과가 몇 행인지 세는 문제가 나옵니다.
1네 연산자가 남기는 행
그림은 두 결과를 먼저 나란히 놓습니다. 왼쪽은 DS를 들은 학번 101·102·103·105, 오른쪽은 NW를 들은 학번 101·103·104·105·107입니다. 101·103·105는 양쪽에 다 있습니다. 이어서 연산자 넷을 차례로 보입니다. UNION은 두 결과를 합치고 겹치는 행을 한 번만 남깁니다. 6행입니다. UNION ALL은 겹치는 행을 지우지 않고 두 결과를 그대로 이어 붙입니다. 4행과 5행을 더해 9행입니다. INTERSECT는 양쪽에 다 있는 행만 남깁니다. 101·103·105, 3행입니다. EXCEPT는 왼쪽 결과에서 오른쪽에도 있는 행을 뺍니다. DS만 들은 102, 1행입니다. Oracle에서는 같은 연산을 MINUS로도 씁니다.
| 연산자 | 남기는 행 | 예제 결과 |
|---|---|---|
| UNION | 두 결과를 합치고 겹치는 행은 한 번만 | 6행 |
| UNION ALL | 겹치는 행을 지우지 않고 그대로 이어 붙인다 | 9행(4행 + 5행) |
| INTERSECT | 양쪽에 다 있는 행만 | 3행(101·103·105) |
| EXCEPT | 왼쪽에서 오른쪽에도 있는 행을 뺀 나머지 | 1행(102) |
2두 SELECT 문이 맞아야 하는 것
두 SELECT 문은 열 개수가 같아야 합니다. 열은 이름이 아니라 순서로 짝을 지으므로, 같은 자리의 열끼리 자료형(숫자·문자)도 맞아야 합니다. 결과의 열 이름은 첫 SELECT 문의 열 이름을 따릅니다. ORDER BY는 집합 연산 전체의 맨 끝에 한 번만 씁니다. 첫 SELECT 문 뒤에 쓰면 오류입니다.
예제
결과 표는 Oracle에서 돌린 결과입니다.
집합 연산의 결과도 행 순서가 정해져 있지 않아서 끝에 ORDER BY 학번을 붙였습니다.
UNION
SELECT 학번
FROM 수강
WHERE 과목코드 = 'DS'
UNION
SELECT 학번
FROM 수강
WHERE 과목코드 = 'NW'
ORDER BY 학번;
| 학번 |
|---|
| 101 |
| 102 |
| 103 |
| 104 |
| 105 |
| 107 |
UNION ALL
SELECT 학번
FROM 수강
WHERE 과목코드 = 'DS'
UNION ALL
SELECT 학번
FROM 수강
WHERE 과목코드 = 'NW'
ORDER BY 학번;
| 학번 |
|---|
| 101 |
| 101 |
| 102 |
| 103 |
| 103 |
| 104 |
| 105 |
| 105 |
| 107 |
INTERSECT
SELECT 학번
FROM 수강
WHERE 과목코드 = 'DS'
INTERSECT
SELECT 학번
FROM 수강
WHERE 과목코드 = 'NW'
ORDER BY 학번;
| 학번 |
|---|
| 101 |
| 103 |
| 105 |
EXCEPT
표준 SQL의 이름은 EXCEPT입니다. Oracle은 오래전부터 MINUS를 써 왔고, 지금 판(23.26)은 EXCEPT도 받습니다. 둘의 뜻은 같습니다.
SELECT 학번
FROM 수강
WHERE 과목코드 = 'DS'
EXCEPT
SELECT 학번
FROM 수강
WHERE 과목코드 = 'NW'
ORDER BY 학번;
| 학번 |
|---|
| 102 |
열 개수와 형
아래처럼 열 개수가 다르면 Oracle은 ORA-01789 오류를 냅니다. 학번(숫자)과 이름(문자)처럼 같은 자리의 형이 다르면 ORA-01790 오류입니다.
SELECT 학번,
점수
FROM 수강
WHERE 과목코드 = 'DS'
UNION
SELECT 학번
FROM 수강
WHERE 과목코드 = 'NW';
시험 포인트
- 필기: UNION은 겹치는 행을 지우고, UNION ALL은 지우지 않습니다. 행 수를 묻는 문제는 겹치는 행을 먼저 찾습니다. INTERSECT는 양쪽에 다 있는 행, EXCEPT는 왼쪽 결과에서 오른쪽에도 있는 행을 뺀 나머지입니다. EXCEPT는 두 SELECT 문의 자리를 바꾸면 결과가 달라집니다. Oracle은 EXCEPT를 MINUS로도 씁니다. 두 SELECT 문은 열 개수와 같은 자리의 자료형이 맞아야 하고, ORDER BY는 맨 끝에 한 번만 씁니다.
- 실기: 결과 행을 그대로 표로 씁니다. 연산자 이름은 UNION, UNION ALL, INTERSECT, EXCEPT(Oracle은 MINUS)로 씁니다.
확인 문제
1. 수강 표에서 점수가 80 이상인 학번을 왼쪽, 자료구조(DS)를 들은 학번을 오른쪽에 두고 INTERSECT로 이었습니다. 80점 이상인 행은 101의 두 행(85·80)·104의 95·105의 두 행(100·90)이고, DS를 들은 학번은 101·102·103·105입니다. 결과로 알맞은 것을 고르세요.
- ① 104 — 1행
- ② 101·105 — 2행
- ③ 101·101·105·105 — 4행
- ④ 101·102·103·104·105 — 5행
정답
②입니다. INTERSECT는 양쪽에 다 있는 행만 남깁니다. 왼쪽 학번은 101·104·105, 오른쪽은 101·102·103·105이므로 양쪽에 다 있는 101·105가 남습니다.
- ① 왼쪽에만 있는 행을 남긴다고 봤습니다. 그것은 EXCEPT의 동작이고, 그렇게 풀면 104가 나옵니다.
- ③ 왼쪽에 101과 105가 두 번씩 있으니 결과에도 두 번씩 나온다고 봤습니다. ALL을 붙이지 않은 집합 연산자는 결과에서 겹치는 행을 지웁니다.
- ④ 두 결과를 합친다고 봤습니다. 그것은 UNION의 동작이고, 그렇게 풀면 다섯 학번이 나옵니다.
2. 다음 (가)~(라)에 맞는 집합 연산자를 쓰세요. 답을 끝까지 쓴 뒤 정답을 펼치세요.
- (가) 학과 표의 학과코드 목록과 학생 표의 학과코드 목록을 이었습니다. 겹치는 코드를 찾아 지우는 일을 하지 않아, 3행과 7행을 그대로 더한 10행이 나왔습니다.
- (나) 같은 두 목록을 이으면서 양쪽에 다 있는 코드는 한 번만 남겼습니다. 결과는 4행입니다.
- (다) 2학년 이하인 학번 목록과 컴퓨터(CS) 학과 학번 목록에서 양쪽에 다 있는 학번만 남겼습니다.
- (라) 학생 표의 학번 목록에서 수강 표의 학번 목록을 뺐습니다. 한 과목도 듣지 않은 학생만 남습니다. 표준 SQL의 이름과 Oracle이 오래 써 온 이름을 함께 쓰세요.
정답
받는 답: (가) UNION ALL · (나) UNION · (다) INTERSECT · (라) EXCEPT(Oracle은 MINUS)
(가)의 실마리는 '겹치는 행을 찾아 지우지 않는다', (나)는 '합치되 한 번만 남긴다', (다)는 '양쪽에 다 있는 것만', (라)는 '왼쪽에서 오른쪽에도 있는 것을 뺀다'입니다.
- 같은 이름의 다른 꼴: (라) MINUS만 쓴 꼴 — Oracle의 이름입니다. 문제가 표준 SQL의 이름과 함께 쓰라고 했습니다.
- 틀리는 꼴 UNION — (가)에: 합치면서 겹치는 행을 한 번만 남깁니다. 10행이 나왔다는 것은 지우지 않았다는 뜻입니다.
- 틀리는 꼴 UNION ALL — (나)에: 겹치는 행을 지우지 않습니다. 학과 쪽 CS·EE와 학생 쪽 CS·EE가 두 번씩 남아 4행이 되지 않습니다.
- 틀리는 꼴 INTERSECT — (라)에: 양쪽에 다 있는 행만 남깁니다. (라)는 한쪽에만 있는 행을 남깁니다.
3. 다음 쿼리의 결과를 표로 쓰세요.
SELECT 학번
FROM 수강
WHERE 과목코드 = 'OS'
UNION
SELECT 학번
FROM 수강
GROUP BY 학번
HAVING AVG(점수) >= 85
ORDER BY 학번;
정답
| 학번 |
|---|
| 101 |
| 102 |
| 104 |
| 105 |
위 SELECT 문은 운영체제를 들은 101·102·104 세 행을 냅니다. 아래 SELECT 문은 학번별 평균이 85 이상인 104(85)·105(95) 두 행을 냅니다. UNION이 둘을 합치고 양쪽에 다 있는 104를 한 번만 남겨 4행이 됩니다. UNION ALL로 바꾸면 104가 두 번 나와 5행입니다.
더 자세히: SQLD의 집합 연산자