MSSQL 2.6 · 2부. 조회 심화

집합 연산

UNION · UNION ALL · INTERSECT · EXCEPT 로 결과끼리 합치고 뺍니다. 여기서는 NULL 을 같다고 보는 것도 함께 봅니다.

예상 학습 시간 18분 난이도 초급
개념 설명

결과끼리 합치고 뺍니다

조인은 테이블을 옆으로 이어 열을 늘립니다(2.1). 집합 연산은 위아래로 이어 행을 늘리거나 걸러 냅니다.

연산하는 일
UNION양쪽을 합치고 중복을 없앱니다
UNION ALL양쪽을 그대로 붙입니다
INTERSECT양쪽에 다 있는 것
EXCEPT왼쪽에만 있는 것

실습 데이터로 확인해 보겠습니다. 게시판마다 글을 작성한 사람이 몇 명인지 먼저 보아 두면 뒤의 결과가 읽힙니다.

게시판작성자 수
공지사항2
자유게시판18
질문답변12
최소 예제

UNION 과 UNION ALL

SQL
-- 중복을 없앱니다
SELECT user_num FROM Board.POSTS WHERE board_num = 1
UNION
SELECT user_num FROM Board.POSTS WHERE board_num = 2;

-- 그대로 붙입니다
SELECT user_num FROM Board.POSTS WHERE board_num = 1
UNION ALL
SELECT user_num FROM Board.POSTS WHERE board_num = 2;
결과 — UNION
행 수
18
결과 — UNION ALL
행 수
350

18 과 350 입니다. 공지 35건과 자유 315건을 합치면 350행인데, UNION 은 같은 사람을 하나로 묶어 18명으로 줄였습니다. 공지를 작성한 두 사람이 자유게시판에도 작성했기 때문에 20 이 아니라 18 입니다.

중복을 없애는 데는 값이 듭니다. 데이터베이스가 결과를 정렬하거나 해시로 묶어 비교해야 하기 때문입니다. 겹칠 일이 없다면 UNION ALL 을 사용하십시오. 게시판별 목록을 이어 붙이는 것처럼 애초에 겹치지 않는 자료라면 그 비용이 순전히 낭비입니다.

상세 사용법

지켜야 하는 규칙

위아래로 잇는 것이므로 열의 개수가 같아야 하고, 자리마다 형식이 맞아야 합니다.

SQL
SELECT num FROM Board.BOARDS
UNION
SELECT num, name FROM Board.BOARDS;
오류
메시지 205 All queries combined using a UNION, INTERSECT or EXCEPT operator must have an equal number of expressions in their target lists.
  • 이름은 맞출 필요가 없습니다. 자리로 짝지어지고, 결과의 열 이름은 첫 번째 조회의 것을 따릅니다.
  • 형식이 다르면 넓은 쪽으로 맞춰집니다. intbigint 를 이으면 bigint 가 되고, 맞출 수 없으면 오류입니다.
  • ORDER BY 는 맨 끝에 한 번만 적습니다. 중간에 적을 수 없습니다. 합친 뒤의 차례를 정하는 것이기 때문입니다.
상세 사용법

INTERSECT 와 EXCEPT

INTERSECT양쪽에 다 있는 것만 냅니다. 공지사항과 질문답변에 모두 글을 작성한 사람을 찾아봅니다.

SQL
SELECT U.nickname
FROM (
    SELECT user_num FROM Board.POSTS WHERE board_num = 1
    INTERSECT
    SELECT user_num FROM Board.POSTS WHERE board_num = 3
) AS T
    INNER JOIN Member.USERS AS U ON U.num = T.user_num
ORDER BY U.nickname;
결과
nickname
한소망
홍길동
(2개 행이 영향을 받음)

공지사항을 작성한 사람이 애초에 이 둘뿐이고, 둘 다 질문답변에도 글을 썼습니다.

EXCEPT왼쪽에서 오른쪽을 뺍니다. 자유게시판에는 작성했지만 공지사항에는 작성하지 않은 사람입니다.

SQL
SELECT COUNT(*) AS 자유만 FROM (
    SELECT user_num FROM Board.POSTS WHERE board_num = 2
    EXCEPT
    SELECT user_num FROM Board.POSTS WHERE board_num = 1
) AS T;
결과
자유만
16
(1개 행이 영향을 받음)

자유게시판 작성자 18명에서 공지도 작성한 2명을 뺀 16명입니다. 차례를 바꾸면 뜻이 달라집니다. 공지에서 자유를 빼면 0 입니다. 공지를 작성한 두 사람이 자유게시판에도 작성했기 때문입니다.

INTERSECTEXCEPT중복을 없앱니다. ALL 을 붙이는 형태는 SQL Server 에 없습니다.

상세 사용법

여기서는 NULL 을 같다고 봅니다

1.9 에서 NULL 은 자기 자신과도 같지 않다고 했습니다. 그런데 집합 연산에서 중복을 없앨 때는 NULL 끼리 같은 것으로 봅니다.

SQL
SELECT CAST(NULL AS INT) AS v
UNION
SELECT CAST(NULL AS INT);

SELECT CAST(NULL AS INT) AS v
UNION ALL
SELECT CAST(NULL AS INT);
결과 — UNION
행 수
1
결과 — UNION ALL
행 수
2

UNIONNULL 둘을 하나로 묶었습니다. INTERSECT 도 마찬가지로 짝을 맞춥니다 — 양쪽이 NULL 이면 같은 것으로 보아 1행을 냅니다.

모순이 아니라 적용되는 자리가 다릅니다.

어디서NULL 을
WHERE · ON비교합니다. 같은지 알 수 없으므로 조건이 참이 못 됩니다(1.6 · 1.9)
UNION · INTERSECT · EXCEPT같은 값인지 가려냅니다. 값이 없다는 점이 같으므로 하나로 봅니다
GROUP BY · DISTINCT집합 연산과 같습니다. NULL 끼리 한 묶음이 됩니다

"조건에서 견줄 때" 와 "같은 값을 골라낼 때" 의 규칙이 다르다고 기억해 두십시오.

상세 사용법

조인·EXISTS 와 비교하면

INTERSECTEXCEPT 로 하는 일은 EXISTS · NOT EXISTS(2.4)로도 됩니다. 무엇이 다를까요.

항목집합 연산EXISTS
중복없애 줍니다바깥 행을 그대로 둡니다
비교 열적은 열 전부가 같아야조건을 자유롭게
다른 열 가져오기안 됩니다바깥에서 마음대로
읽기"이 목록에서 저 목록을 뺀다""이런 것이 없는 행"

목록끼리 비교하는 것이 뜻에 가까우면 집합 연산이 읽기 좋습니다. 거기에 다른 열을 붙여야 한다면 위 예제처럼 결과를 다시 조인하거나, 처음부터 EXISTS 로 적으십시오.

실습 문제

직접 해보기

1. 글도 쓰고 댓글도 단 사람 난이도 하

글을 작성한 적도 있고 댓글을 단 적도 있는 회원이 몇 명인지 세어 보세요.

SELECT COUNT(*) AS 둘다한사람 FROM ( SELECT user_num FROM Board.POSTS INTERSECT SELECT user_num FROM Board.COMMENTS ) AS T; -- 20 이 나옵니다. 스무 명 모두 글과 댓글을 남겼습니다. -- EXISTS 로 적으면 이렇습니다. -- SELECT COUNT(*) FROM Member.USERS AS U -- WHERE EXISTS (SELECT 1 FROM Board.POSTS P WHERE P.user_num = U.num) -- AND EXISTS (SELECT 1 FROM Board.COMMENTS C WHERE C.user_num = U.num);
2. 두 목록을 한 화면에 난이도 중

공지사항 글 3건과 자유게시판 글 3건을 한 결과로 내되, 어느 게시판 것인지 구분할 수 있게 해보세요.

고정된 값을 열로 넣을 수 있습니다(1.5). 겹칠 일이 없는 자료이므로 중복 제거도 필요 없습니다.
SELECT N'공지' AS 구분, num, title FROM (SELECT TOP 3 num, title FROM Board.POSTS WHERE board_num = 1 ORDER BY num) AS A UNION ALL SELECT N'자유', num, title FROM (SELECT TOP 3 num, title FROM Board.POSTS WHERE board_num = 2 ORDER BY num) AS B ORDER BY 구분, num; -- TOP 과 ORDER BY 를 함께 쓰려면 안쪽에 넣어야 합니다. -- 바깥의 ORDER BY 는 합친 결과 전체에 걸립니다. -- 겹칠 일이 없으므로 UNION ALL 입니다.
요약
  • 조인은 옆으로, 집합 연산은 위아래로 잇습니다.
  • UNION 은 중복을 없애느라 값이 듭니다. 겹칠 일이 없으면 UNION ALL 입니다.
  • 열 개수가 같아야 하고(오류 205), 이름은 첫 번째 조회의 것을 따릅니다.
  • ORDER BY맨 끝에 한 번만 적습니다.
  • 집합 연산은 NULL 을 같다고 봅니다. WHERE 에서 견줄 때와 규칙이 다릅니다.
  • EXCEPT차례가 뜻을 바꿉니다. 왼쪽에서 오른쪽을 뺍니다.