UNION은 두 SELECT 결과를 합친 뒤 전체 출력 행의 중복을 제거하고, UNION ALL은 같은 행도 그대로 보존합니다. ID 열 하나가 같다는 이유만으로 중복 처리하지는 않습니다.

두 연산 모두 결과 행의 반환 순서를 보장하지 않습니다. 특정 순서가 필요하면 최종 결과에 ORDER BY를 명시해야 합니다.

중복은 ID가 아니라 출력된 한 행 전체로 판단합니다

PostgreSQL 18에서 UNION은 두 질의의 결과 행을 합친 뒤 중복 행을 제거합니다. UNION ALL은 그 제거 단계를 생략해 입력에 있던 행 수를 그대로 보존합니다. 여기서 비교 단위는 SELECT 목록에 나온 모든 열의 조합입니다. 첫 열인 ID만 같고 이름 열이 다르면 서로 다른 행입니다. 반대로 두 열이 모두 같으면 서로 다른 원본 표에서 나왔더라도 같은 출력 행으로 취급합니다.

두 SELECT는 같은 개수의 열을 내고, 같은 위치에 놓인 열끼리 서로 호환되는 자료형이어야 합니다. 첫 SELECT의 열 이름이 결과 이름을 정하므로 두 번째 SELECT의 열 순서도 맞춰야 합니다. UNION은 중복을 제거하는 집합 연산이고, UNION ALL은 두 결과를 이어 붙여 중복을 보존하는 연산이라고 먼저 구분하면 계산 실수를 줄일 수 있습니다.

작은 결과를 먼저 합치고 튜플별 개수를 셉니다

다음은 출고 표와 반품 표에서 같은 열인 상품 번호와 등급을 가져온다고 가정한 교육용 예시입니다. 실제 시험 기출이나 AnkiPass 유료 카드가 아닙니다. 왼쪽 결과에는 (1,A)가 두 번 있고, 오른쪽 결과에도 같은 튜플이 한 번 있습니다. 중복 제거 전에 전체 행을 세면 다섯 행입니다.

UNION 연산 전 입력 결과를 직접 센 예시
입력 결과행나온 횟수
출고(1,A)2
출고(2,B)1
반품(1,A)1
반품(3,C)1

이제 튜플별로 묶습니다. (1,A)는 두 결과를 통틀어 세 번, (2,B)는 한 번, (3,C)도 한 번 나옵니다. 따라서 UNION ALL은 3+1+1로 5행을 반환합니다. UNION은 각 튜플을 한 번씩만 남겨 (1,A), (2,B), (3,C)의 3행을 반환합니다. 같은 ID 1의 행이 세 개라고 해서 모두 같다고 볼 수 있는 까닭은 이름 열까지 A로 같기 때문입니다.

PostgreSQL 18 집합 연산을 읽는 형태이며 서버에서는 실행하지 않았습니다
SELECT product_id, grade FROM shipment_rows
UNION
SELECT product_id, grade FROM return_rows;

SELECT product_id, grade FROM shipment_rows
UNION ALL
SELECT product_id, grade FROM return_rows;

행 개수와 반환 순서는 별개로 확인합니다

UNION이 결과를 붙이는 과정에서 왼쪽 질의의 행이 항상 먼저 온다고 기대하면 안 됩니다. PostgreSQL 문서는 UNION이 입력을 사실상 이어 붙이지만 실제 반환 순서는 보장하지 않는다고 명시합니다. 또한 ORDER BY가 없는 질의 결과는 순서가 정해져 있지 않습니다. 한 번 실행했을 때 특정 순서로 보였다는 사실은 다음 실행에서도 같은 순서를 받는다는 약속이 아닙니다.

번호와 등급 순으로 화면에 보여 주려면 집합 연산이 끝난 전체 결과에 ORDER BY product_id, grade를 둡니다. 시험에서 '몇 행인가'를 묻는 단계에는 중복 제거 여부만 적용하고, '어떤 순서로 출력되는가'를 물으면 ORDER BY가 있는지 다시 살펴보세요. ORDER BY가 있더라도 정렬 키가 같은 행끼리의 세부 순서는 별도 고유 키를 넣지 않는 한 정해지지 않을 수 있습니다.

계산 순서를 고정하면 흔한 오답을 피할 수 있습니다

  1. 양쪽 SELECT가 뽑는 열 이름과 순서를 확인합니다.
  2. 각 입력의 행을 세되 아직 중복을 지우지 않습니다.
  3. UNION이면 전체 열 값이 같은 행끼리 묶고 각 묶음을 1개로 줄입니다. UNION ALL이면 원래 횟수를 유지합니다.
  4. 최종 정렬 요구는 ORDER BY 유무로 별도 판정합니다.

가장 흔한 실수는 ID가 같으면 중복이라고 판단하는 것입니다. 예를 들어 (1,A)와 (1,B)는 ID가 같아도 두 번째 열이 다르므로 다른 행입니다. 반대로 서로 다른 원본 표에서 나온 (1,A)는 두 열이 같으므로 UNION에서 하나만 남습니다. 또 UNION의 '집합'이라는 말을 보고 자동으로 오름차순 정렬된다고 생각하기 쉽지만, 중복 제거와 정렬 보장은 서로 다른 기능입니다.

정답을 가리고 행 수를 먼저 떠올려 보세요

왼쪽 SELECT가 (7,X), (7,Y)를 반환하고 오른쪽 SELECT도 (7,X), (7,Y)를 반환한다고 합시다. 전체 입력은 네 행입니다. UNION ALL은 네 행을 모두 남깁니다. UNION은 (7,X)와 (7,Y)의 두 행만 남깁니다. 여기서도 두 행은 첫 열이 같지만 두 번째 열이 달라 서로 다릅니다. 이 결과를 다시 실행할 때 순서까지 고정하려면 최종 ORDER BY에 두 열을 지정해야 합니다.

정답을 가리고 확인해 보세요

이 글을 위해 만든 학습 예시이며 공식 기출문제나 판매 카드의 인용이 아닙니다.

왼쪽 결과가 (4,P), (4,P), (4,Q)이고 오른쪽 결과가 (4,P), (5,R)입니다. UNION ALL과 UNION의 행 수는 각각 몇 개이며, 기본 반환 순서를 단정할 수 있나요?

정답과 해설 확인UNION ALL은 5행, UNION은 (4,P), (4,Q), (5,R)로 3행입니다. 두 결과 모두 ORDER BY가 없으면 반환 순서를 단정할 수 없습니다.

(4,P)는 양쪽 합쳐 세 번 나오지만 UNION에서는 한 번만 남습니다. (4,Q)와 (5,R)는 각기 다른 전체 행이므로 한 번씩 남습니다. UNION ALL은 중복을 보존합니다. PostgreSQL 문서는 집합 연산 결과의 정렬을 보장하지 않으므로 명시적 ORDER BY가 필요합니다.