[SQLD 12화] UNION과 UNION ALL의 문법적 차이점과 대용량 데이터 집합 연산 최적화 전략 (SQLD, 직장인 SQL)

1. 데이터 집합을 세로로 확장하기: 집합 연산의 개념

11화에서 우리는 FROM 절 내부에서 가상의 테이블 역할을 수행하며 복잡하게 꼬인 스파게티 쿼리를 단계별로 깔끔하게 쪼개어 가독성을 높이는 인라인 뷰(Inline View)의 문법과 리팩토링 전략을 살펴보았습니다. 인라인 뷰나 조인(Join)이 테이블을 가로 방향으로 결합하여 칼럼을 확장하는 방식이라면, 실무에서는 성격이 유사한 별개의 데이터 집합들을 ‘세로 방향(행 기준)으로 일렬로 이어 붙여야 하는’ 비즈니스 상황도 빈번하게 발생하곤 합니다.

가령 “과거 행정 구역 개편 전 데이터와 개편 후 데이터를 하나의 테이블로 통합해 달라”거나 “온라인 매장에서 발생한 매출 내역과 오프라인 매장에서 발생한 매출 내역을 세로로 길게 결합하여 전사 통합 실적 데이터를 생성해 달라”는 식의 요청이 대표적입니다.

이처럼 두 개 이상의 독립된 SELECT문 조회 결과를 하나의 결과 집합으로 깔끔하게 결합해 주는 SQL의 도구가 바로 집합 연산자(Set Operator)이며, 그중 가장 높은 빈도로 활용되는 명령어가 UNIONUNION ALL입니다. 본 연작 시리즈는 대용량 처리에 널리 쓰이는 테라데이터 SQL 환경을 기준으로 두 연산자의 문법 규칙과 최적화 전략을 설명하겠습니다.

2. 집합 연산의 필수 규칙: 칼럼 구조의 일치성

UNION이나 UNION ALL을 사용해 데이터 집합을 위아래로 결합할 때, 데이터베이스 엔진이 오류 없이 명령을 수행하기 위해서는 결합 대상이 되는 모든 SELECT문이 다음과 같은 구조적 대칭성을 정확하게 만족해야 합니다.

  1. 칼럼 개수의 일치: 첫 번째 SELECT 절에 나열된 칼럼이 3개라면, 결합되는 두 번째, 세 번째 SELECT 절의 칼럼 개수 역시 반드시 정확히 3개로 일치해야 합니다.
  2. 데이터 타입의 호환성: 위아래로 매칭되어 겹쳐지는 칼럼들의 데이터 유형(형식)이 동일하거나 암시적 형변환이 가능한 호환되는 유형이어야 합니다. 첫 번째 집합의 첫 칼럼이 숫자형(INTEGER)인데 두 번째 집합의 첫 칼럼이 순수 문자형(VARCHAR)이라면 시스템은 데이터 타입 불일치 에러를 발생시킵니다.
  3. 순서의 일치: 칼럼의 순서에 따라 위아래 데이터가 그대로 포개어지므로, 순서가 뒤바뀌면 이름 자리에 금액이 들어가고 금액 자리에 이름이 들어가는 비즈니스적 왜곡이 발생하곤 합니다.
-- UNION 및 UNION ALL의 가장 표준적인 문법 구조
SELECT PROD_CD, SALE_AMT FROM DW_MART.ON_SALES
UNION ALL
SELECT PROD_CD, SALE_AMT FROM DW_MART.OFF_SALES;

3. 현업 비즈니스 사례로 보는 UNION과 UNION ALL의 본질적 차이

두 연산자의 문법 규칙은 동일하지만, 결합된 최종 데이터를 화면에 도출할 때 중복된 데이터를 제어하느냐 허용하느냐에 따라 비즈니스 결과와 시스템 부하 면에서 거대한 차이가 발생합니다. 이해를 돕기 위해 실제 유통 기업 마케팅 부서에서 발생할 수 있는 가상의 시나리오를 살펴보겠습니다.

📊 [실무 사례 예시: 마케팅 팀 최 대리의 통합 타겟팅 명단 추출]

백화점 마케팅 팀의 최 대리는 이번 주말 대규모 기획전을 앞두고 “7월 11일 당일에 ‘식품’ 카테고리를 구매한 고객 명단‘리빙’ 카테고리를 구매한 고객 명단을 위아래로 통합하여, 기획전 안내 문자 발송을 위한 단 하나의 최종 대상자 명단을 추출해 달라”는 요청을 받았습니다.

두 카테고리를 모두 구매한 열혈 고객이 존재할 수 있는 상황이었습니다. 최 대리는 아래와 같이 두 가지 형태의 집합 연산 쿼리를 고려해 볼 수 있습니다.

1) 중복을 엄격하게 제거하는 UNION (Distinct)

-- [UNION 예시] 위아래 집합을 합친 후 중복된 행을 자동으로 제거하는 쿼리
SELECT CUST_NO FROM DW_MART.FOOD_PURCHASE_LOG WHERE SALE_DT = '2026-07-11'
UNION
SELECT CUST_NO FROM DW_MART.LIVING_PURCHASE_LOG WHERE SALE_DT = '2026-07-11';
  • 작동 결과: 만약 ‘김 회원’이 7월 11일에 식품도 사고 리빙 상품도 구매하여 양쪽 테이블에 모두 회원번호가 존재하더라도, UNION 연산자는 최종 결과 화면에 김 회원의 번호를 단 1건만 남기고 중복을 완전히 제거하여 노출해 줍니다.
  • 비즈니스적 판단: 마케팅 문자 발송 시 동일 고객에게 문자가 두 번 중복 발송되어 예산이 낭비되거나 고객 불만이 발생하는 리스크를 예방하고 싶을 때 매우 유용하고 안전한 선택으로 평가받습니다.

2) 중복 제거 없이 그대로 결합하는 UNION ALL (Allow Duplicates)

-- [UNION ALL 예시] 중복 제거 과정 없이 두 집합을 순서대로 이어 붙이는 쿼리
SELECT CUST_NO FROM DW_MART.FOOD_PURCHASE_LOG WHERE SALE_DT = '2026-07-11'
UNION ALL
SELECT CUST_NO FROM DW_MART.LIVING_PURCHASE_LOG WHERE SALE_DT = '2026-07-11';
  • 작동 결과: 중복 체크 단계를 생략하기 때문에 식품에서 추출된 데이터와 리빙에서 추출된 데이터를 있는 그대로 세로로 이어 붙입니다. 따라서 식품과 리빙을 모두 구매한 김 회원의 데이터는 최종 결과 집합에 2행(Row)으로 중복되어 노출됩니다.
  • 비즈니스적 판단: 단순히 전체 거래 건수를 누적하여 합산해야 하거나, 중복 데이터가 발생할 리가 없는 완전히 독립된 데이터셋(예: 2025년 매출과 2026년 매출)을 합칠 때 직관적인 대안으로 활용됩니다.

4. 대용량 데이터웨어하우스 환경에서의 성능 최적화 전략

문법적 차이를 넘어, 현직 데이터 분석가들이 대용량 조회 환경에서 두 명령어를 구별하여 사용하는 가장 결정적인 이유는 ‘시스템 자원 소모량과 실행 속도의 격차’에 있습니다.

  • UNION의 무거운 연산 비용: UNION 명령어는 단순히 두 집합을 합치는 데 그치지 않고, 최종 결과물을 사용자에게 내어주기 전 데이터베이스 내부적으로 모든 행을 대조하여 똑같은 내용이 있는지 찾아내고 지워버리는 추가 연산 과정을 거치게 됩니다. 이는 시스템 입장에서는 상당한 메모리 자원을 소모하는 무거운 작업이며, 데이터의 행 수가 수천만 건을 넘어가면 쿼리 처리 시간이 급격히 늘어나거나 일시적인 처리 지연 현상을 유발하는 경향이 있는 것으로 알려져 있습니다.
  • UNION ALL의 가벼운 효율성: 반면 UNION ALL은 중복 제거 과정을 수행하지 않기 때문에 UNION보다 훨씬 적은 연산으로 결과를 반환합니다. 특히 대용량 데이터에서는 중복 제거를 위한 Sort 또는 Hash연산이 생략되어 실행 시간이 크게 단축되는 경우가 많습니다.

💡 현직 분석가의 최적화 치트키: UNION ALL 선제적 유도

중복 제거가 반드시 필요하지 않다면 UNION ALL을 사용하는 것이 일반적으로 유리합니다. 중복 제거가 필요한 경우에는 데이터 특성과 실행 계획을 고려하여 UNION 또는 다른 방식(NOT EXISTS, DISTINCT 등)을 선택하는 것이 좋습니다.

-- [최적화 예시] 데이터 자체의 중복을 필터로 격리한 뒤 UNION ALL로 속도를 극대화한 쿼리
SELECT CUST_NO, 'FOOD' AS GRP FROM DW_MART.FOOD_PURCHASE_LOG WHERE SALE_DT = '2026-07-11'
UNION ALL
-- 식품 테이블에 존재하지 않는 리빙 구매 고객만 골라내어 중복 체크 비용을 우회
SELECT CUST_NO, 'LIVING' AS GRP FROM DW_MART.LIVING_PURCHASE_LOG L
WHERE SALE_DT = '2026-07-11'
  AND NOT EXISTS (
      SELECT 1 FROM DW_MART.FOOD_PURCHASE_LOG F 
      WHERE F.SALE_DT = '2026-07-11' AND F.CUST_NO = L.CUST_NO
  );

5. 12화 요약 및 실무 집합 연산 자가 진단 체크리스트

쿼리를 최종 실행 환경에 상정하기 전, 데이터 누락이나 연산 왜곡의 결점이 없는지 스스로 진단할 수 있는 요약 가이드를 제공합니다.

체크포인트발생 가능한 실무 리스크현직 분석가의 권장 대안
습관적인 UNION 명령어 사용 여부중복 검사 연산으로 인해 대용량 데이터 조회 시 심각한 속도 저하 유발중복 데이터가 없거나 수용 가능한 구조라면 가급적 UNION ALL 선택
위아래 SELECT문 칼럼 개수 및 타입칼럼 개수 또는 데이터 타입 불일치 오류 발생콤마(,) 기준 칼럼 배치 순서와 데이터 형식 매칭 상태 전수 조사
최종 정렬 명령어(ORDER BY)의 위치각각의 SELECT문 내에 개별 작성 시 문법 에러ORDER BY 구문은 전체 결합이 끝난 쿼리의 가장 마지막 행에 단 1회만 기재

6. 결론 및 다음 화 예고

이번 12화에서는 행(Row)을 이어 붙여 하나의 결과 집합(Result Set)을 만드는 UNIONUNION ALL 연산자의 문법 구조를 살펴보고, 중복 제거 연산 유무가 대용량 데이터 환경에서 시스템 성능과 비즈니스 결과물에 미치는 영향을 다각도로 점검해 보았습니다. 데이터셋의 구조적 결합 규칙을 명확히 인지하고 자원 소모를 최소화하는 방향으로 집합 연산을 설계하는 역량은 정교한 분석가로 거듭나기 위한 이정표가 됩니다.

지금까지 1화부터 12화에 이르기까지 우리는 행 데이터를 추출하고, 결합하고, 집합을 합치는 전반적인 데이터 핸들링 기법을 섭렵했습니다.

이어지는 13화부터는 개별 상세 데이터(Raw Data) 파편들을 그룹별로 묶어 비즈니스 지표를 산출해 내는 통계 분석의 핵심인 GROUP BY와 기본 집계 함수의 세계로 진입합니다. 부서별, 월별 매출 요약을 계산하는 COUNT, SUM, MIN, MAX 함수의 문법 구조를 분석하고, 6화에서 배웠던 NULL 개념이 집계 함수 내부 연산에 유입되었을 때 어떻게 자동으로 처리되는지 그 상세한 메커니즘을 실제 비즈니스 사례를 통해 살펴보겠습니다.

댓글 남기기