본문 바로가기
데이터베이스(SQL)/데이터베이스 개념

SQL UNION과 UNION ALL 차이: 중복 제거·정렬·성능 선택 기준

by char_lie 2026. 9. 22.
반응형

여러 조회 결과를 세로로 이어 붙일 때는 UNION 또는 UNION ALL을 사용한다. 두 연산자의 핵심 차이는 간단하다.

  • UNION은 합친 결과에서 중복 행을 제거한다.
  • UNION ALL은 중복을 포함해 모든 행을 유지한다.

하지만 실제 쿼리에서는 “중복”의 기준, 결과 정렬, 처리 비용까지 함께 봐야 한다. 같은 데이터처럼 보여도 선택한 열이 하나만 달라지면 UNION은 두 행을 모두 남긴다. 반대로 정렬을 쓰지 않으면 위쪽 SELECT의 행이 먼저 나온다고 보장할 수도 없다.

먼저 결과 차이를 확인한다

다음 예제는 회원 목록 두 개를 합친다. 두 목록에는 (3, 'Carol')이 공통으로 들어 있다.

WITH first_group(id, name) AS (
    VALUES (1, 'Alice'),
           (2, 'Bob'),
           (3, 'Carol')
),
second_group(id, name) AS (
    VALUES (3, 'Carol'),
           (4, 'David'),
           (5, 'Eve')
)
SELECT id, name FROM first_group
UNION
SELECT id, name FROM second_group
ORDER BY id;

UNION 결과는 5행이다.

idname

1 Alice
2 Bob
3 Carol
4 David
5 Eve

같은 쿼리에서 UNION만 UNION ALL로 바꾸면 6행이 된다.

WITH first_group(id, name) AS (
    VALUES (1, 'Alice'),
           (2, 'Bob'),
           (3, 'Carol')
),
second_group(id, name) AS (
    VALUES (3, 'Carol'),
           (4, 'David'),
           (5, 'Eve')
)
SELECT id, name FROM first_group
UNION ALL
SELECT id, name FROM second_group
ORDER BY id;

이번에는 (3, 'Carol')이 두 번 나온다. UNION ALL에서 ALL은 각 조회가 반환한 행의 개수를 그대로 보존한다는 뜻이다.

중복은 선택한 열 전체로 판단한다

UNION이 제거하는 중복은 특정 ID의 중복이 아니라 결과 행 전체의 중복이다.

SELECT 3 AS id, 'Carol' AS name
UNION
SELECT 3 AS id, 'Caroline' AS name;

두 행은 id가 같아도 name이 다르므로 모두 남는다.

3 | Carol
3 | Caroline

고객 ID별로 한 행만 필요하다고 해서 UNION만 붙이면 해결되지 않는 이유다. 업무상 중복 기준이 id라면 먼저 “어느 이름을 남길지”를 정해야 한다. 갱신 시각이 가장 최근인 행을 고르거나, 우선순위가 높은 원본을 선택하는 식의 별도 규칙이 필요하다.

반대로 결과에 불필요한 열을 추가하면 원래 제거되던 행이 서로 다른 행이 될 수 있다.

SELECT 3 AS id, 'Carol' AS name, 'first' AS source
UNION
SELECT 3 AS id, 'Carol' AS name, 'second' AS source;

source 값이 다르기 때문에 결과는 2행이다. 원본 구분 열도 유지하면서 실제 데이터만 중복 제거해야 한다면, 합친 뒤 원하는 열과 우선순위를 기준으로 다시 정리해야 한다.

정렬은 마지막 ORDER BY로 명시한다

UNION이 중복을 제거하는 과정에서 정렬과 비슷한 처리가 보일 수 있다. 그렇다고 결과가 특정 순서로 나온다는 뜻은 아니다. UNION ALL도 첫 번째 조회 결과 다음에 두 번째 조회 결과가 항상 이어진다고 가정하면 안 된다.

최종 표시 순서가 필요하면 결합된 결과에 ORDER BY를 적용한다.

SELECT id, name FROM first_group
UNION ALL
SELECT id, name FROM second_group
ORDER BY id, name;

원본별 순서가 필요하다면 정렬용 값을 결과에 포함하는 방법이 명확하다.

SELECT 1 AS source_order, id, name
FROM first_group

UNION ALL

SELECT 2 AS source_order, id, name
FROM second_group

ORDER BY source_order, id;

화면에는 source_order가 필요 없다면 바깥 조회로 감출 수 있다.

SELECT id, name
FROM (
    SELECT 1 AS source_order, id, name FROM first_group
    UNION ALL
    SELECT 2 AS source_order, id, name FROM second_group
) AS combined
ORDER BY source_order, id;

각 SELECT 안에 따로 적은 ORDER BY로 전체 결과 순서를 만들려고 하지 않는 편이 안전하다. 최종 결과의 순서는 가장 바깥쪽 ORDER BY로 결정한다.

열 개수와 자료형도 맞아야 한다

두 조회는 같은 개수의 열을 반환해야 하고, 같은 위치의 열끼리 호환되는 자료형이어야 한다.

SELECT id, name FROM first_group
UNION ALL
SELECT id FROM second_group;

위 쿼리는 왼쪽은 2열, 오른쪽은 1열이라 실행할 수 없다. 열 이름보다 위치가 중요하다는 점도 주의한다.

SELECT id, name FROM first_group
UNION ALL
SELECT id, name FROM second_group;

첫 번째 열끼리, 두 번째 열끼리 결합된다. 실제 테이블에서 SELECT *를 사용하면 스키마 변경으로 열 순서나 개수가 달라질 수 있으므로 결합할 열을 명시하는 편이 안전하다. 필요한 경우 CAST로 자료형도 의도적으로 맞춘다.

UNION ALL이 대체로 더 단순한 이유

UNION ALL은 각 결과를 유지한 채 결합하면 된다. UNION은 여기에 전체 행의 중복을 판별하고 제거하는 작업이 추가된다. 데이터가 많을수록 비교, 정렬 또는 해시 처리에 메모리와 CPU가 더 들 수 있다.

다만 “UNION ALL은 언제나 몇 배 빠르다”처럼 고정된 수치로 말할 수는 없다. 실제 비용은 데이터 양, 중복 비율, 인덱스, 메모리 설정, 병렬 처리와 DBMS의 실행 계획에 따라 달라진다.

실제 쿼리에서는 실행 계획으로 확인한다.

EXPLAIN
SELECT id, name FROM first_group
UNION
SELECT id, name FROM second_group;

그리고 같은 조건에서 UNION ALL의 계획과 비교한다. 운영 환경에서는 대표 데이터로 실행 시간뿐 아니라 읽은 행 수, 정렬이나 해시 단계, 메모리와 임시 디스크 사용 여부도 함께 본다.

중복이 필요 없다는 이유만으로 습관적으로 UNION을 쓰는 것도, 성능만 보고 무조건 UNION ALL을 쓰는 것도 좋은 기준은 아니다. 먼저 결과에 중복 행이 존재해도 되는지를 결정해야 한다.

어떤 상황에서 무엇을 선택할까

UNION이 맞는 경우

  • 서로 겹칠 수 있는 두 결과를 하나의 고유 목록으로 만들어야 한다.
  • 완전히 같은 결과 행은 한 번만 보여야 한다.
  • 중복 제거 자체가 요구사항이며 그 비용을 감수할 수 있다.

예를 들어 두 캠페인의 이메일 주소 목록을 합쳐 한 사람에게 한 번만 안내해야 한다면 고유 목록이 필요하다. 다만 이메일 외의 열을 함께 선택하면 행 전체가 달라질 수 있으므로 결과 열을 신중하게 구성해야 한다.

UNION ALL이 맞는 경우

  • 로그, 거래, 방문 기록처럼 같은 값의 반복도 의미가 있다.
  • 두 원본의 범위가 애초에 겹치지 않는다.
  • 합친 뒤 GROUP BY나 별도의 규칙으로 집계·중복 처리를 한다.
  • 중복 제거가 요구사항이 아니며 불필요한 비용을 피하고 싶다.

월별 파티션에서 8월 데이터와 9월 데이터를 합치거나, 성공 로그와 실패 로그를 모두 모으는 경우에는 각 행을 보존하는 UNION ALL이 자연스럽다.

UNION으로 데이터 문제를 가리지 않는다

조인 조건이 잘못돼 행이 늘어난 결과를 UNION으로 감추면 원인을 놓칠 수 있다. UNION은 결합 결과에서 완전히 같은 행을 없앨 뿐, 잘못된 조인이나 업무 키 중복을 해결하지 않는다.

예상보다 행이 많다면 다음 순서로 확인한다.

  1. 각 SELECT를 따로 실행해 행 수를 확인한다.
  2. 조인 키가 원래 유일해야 하는지 확인한다.
  3. 두 결과 사이에서 어떤 열 조합이 실제로 겹치는지 확인한다.
  4. 중복이 오류인지, 보존해야 하는 기록인지 결정한다.
  5. 그 결정에 따라 UNION 또는 UNION ALL을 선택한다.

선택 기준 한 번에 정리

확인할 질문UNIONUNION ALL

완전히 같은 행 한 행만 유지 모두 유지
중복 판단 기준 선택한 열 전체 판단하지 않음
추가 처리 중복 제거 필요 단순 결합 중심
결과 순서 보장하지 않음 보장하지 않음
정렬 방법 최종 ORDER BY 최종 ORDER BY
대표 용도 고유 목록 로그·거래·분할 데이터 결합

가장 실용적인 기준은 “중복 제거가 비즈니스 요구사항인가”다. 필요하면 UNION, 필요하지 않거나 중복 자체가 정보라면 UNION ALL을 선택한다. 그리고 어느 쪽이든 결과 순서가 중요하면 마지막에 ORDER BY를 명시한다.

반응형

댓글