본문 바로가기
CS/Database

[MySQL] UNION / UNION ALL

by clolee 2025. 4. 13.

✅ UNION / UNION ALL 완전 정복


🧠 1. 기본 개념

연산자 설명
UNION 중복 제거하면서 두 SELECT 결과를 수직으로 합침
UNION ALL 중복 포함한 채로 두 SELECT 결과를 수직으로 합침

🔻 공통점:

  • SELECT 결과의 컬럼 수와 순서, 타입이 같아야

✅ 2. 기본 사용 예시

📍 예시 테이블

-- 테이블 1: 고객 문의
SELECT '문의' AS type, user_id, created_at FROM inquiries

-- 테이블 2: 고객 불만 접수
SELECT '불만' AS type, user_id, created_at FROM complaints

✅ UNION

SELECT '문의' AS type, user_id, created_at FROM inquiries
UNION
SELECT '불만' AS type, user_id, created_at FROM complaints;
  • 중복된 (user_id, created_at) 값은 한 번만 표시됨

✅ UNION ALL

SELECT '문의' AS type, user_id, created_at FROM inquiries
UNION ALL
SELECT '불만' AS type, user_id, created_at FROM complaints;
  • 중복도 전부 포함 → 성능 빠름

✅ 3. 컬럼 수/타입이 다르면 오류

-- ❌ 오류: 컬럼 수가 다름
SELECT id, name FROM users
UNION
SELECT email FROM newsletters;

✔️ 반드시 컬럼 수, 타입 맞춰줘야 함

-- ✅ 해결 방법
SELECT id, name FROM users
UNION
SELECT NULL, email FROM newsletters;

✅ 4. 정렬 적용

  • ORDER BYUNION 전체 결과에 한 번만 사용
  • UNION ALL 이후 정렬하고 싶다면 전체 쿼리를 서브쿼리로 감싸야 함
SELECT name, email FROM customers
UNION ALL
SELECT name, email FROM suppliers
ORDER BY name;  -- ✅ 가능

-- 또는
SELECT * FROM (
  SELECT name, email FROM customers
  UNION ALL
  SELECT name, email FROM suppliers
) AS all_people
ORDER BY name;

✅ 5. 실무 예제


📌 예제 1: 전체 활동 이력 통합 조회

SELECT user_id, '로그인' AS activity, login_time AS event_time FROM user_logins
UNION ALL
SELECT user_id, '주문' AS activity, order_date FROM orders
UNION ALL
SELECT user_id, '문의' AS activity, inquiry_date FROM inquiries
ORDER BY user_id, event_time DESC;

📌 여러 테이블의 활동 기록을 하나의 타임라인으로 합침


📌 예제 2: 탈퇴 회원 + 휴면 회원 조회 (중복 제거 필요)

SELECT id, email FROM withdrawn_users
UNION
SELECT id, email FROM dormant_users;

📌 동일 회원이 탈퇴 후 휴면으로 다시 등록된 경우 → 중복 제거됨


📌 예제 3: 중복 포함 매출 로그 통합

SELECT * FROM online_sales
UNION ALL
SELECT * FROM offline_sales;

📌 매출 내역은 중복되더라도 모두 집계가 중요UNION ALL


✅ 6. 성능 비교: UNION vs UNION ALL

항목 UNION UNION ALL
중복 제거
정렬 발생 ✅ (암묵적)
속도 느림 빠름
실무 사용 빈도 중복 방지 필요 시 사용 기본값으로 더 자주 사용됨

⚠️ 성능 주의

  • UNION은 내부적으로 DISTINCT + 정렬 연산이 포함되어 매우 느릴 수 있음
  • 행 수가 많은 쿼리라면 UNION ALL을 기본으로 사용하고, 필요한 경우만 DISTINCT 처리

✅ 7. 실무에서 자주 쓰는 상황 정리

상황 사용 예시
여러 이력 테이블 통합 UNION ALL
중복 방지된 결과로 통합 조회 UNION
JOIN 대신 풀아웃터 조인 구현 LEFT JOIN UNION RIGHT JOIN
다국적 이메일 리스트 합치기 UNION ALL
A 또는 B 조건 만족한 사용자 조회 UNION 또는 OR, 필요 시 UNION 사용

✅ 8. FULL OUTER JOIN 대체 패턴 (복습)

MySQL에는 FULL OUTER JOIN이 없기 때문에, 다음과 같이 UNION으로 대체함:

-- A에는 있지만 B에는 없는 경우 포함
SELECT a.id, a.value, b.value
FROM a
LEFT JOIN b ON a.id = b.id

UNION

-- B에는 있지만 A에는 없는 경우 포함
SELECT a.id, a.value, b.value
FROM a
RIGHT JOIN b ON a.id = b.id;

📌 LEFT JOIN + RIGHT JOINUNION으로 합쳐서 FULL OUTER JOIN 흉내냄


✅ 9. 정리 요약

항목 UNION UNION ALL
중복 제거 O X
정렬 포함됨? 암묵적 있음 (DISTINCT) 없음
성능 느림 빠름
주 용도 결과 중복 제거 필요 시 로그, 이력 통합 등 일반적인 합치기
실무 팁 기본은 UNION ALL, 필요 시 DISTINCT으로 정제


✅ UNION / UNION ALL에서 컬럼 수 & 타입 제한

조건 설명
📌 컬럼 수 같아야 함 SELECT 절의 컬럼 개수가 동일해야 함
📌 컬럼 순서 같아야 함 1번째, 2번째… 같은 순서로 비교됨
📌 데이터 타입 호환돼야 함 같은 위치의 컬럼끼리 유사한 타입이어야 함 (예: INT와 VARCHAR 같이 사용 ❌)

이건 SQL 표준이기 때문에 MySQL뿐 아니라 Oracle, PostgreSQL, SQL Server 모두 똑같이 적용됨.


❌ 잘못된 예시: 컬럼 수 안 맞음

SELECT id, name FROM users
UNION ALL
SELECT email FROM subscribers;
-- 🚨 오류: SELECT 절의 컬럼 개수가 다릅니다

✅ 해결 예시: NULL 또는 상수로 맞추기

SELECT id, name FROM users
UNION ALL
SELECT NULL AS id, email AS name FROM subscribers;
  • 이렇게 누락된 컬럼에는 NULL이나 상수값을 넣어서 맞춰줌
  • 실무에서 종종 쓰이는 패턴

❌ 잘못된 예시: 타입 불일치

SELECT id, name FROM users          -- id: INT, name: VARCHAR
UNION ALL
SELECT name, birth_date FROM people -- name: VARCHAR, birth_date: DATE
  • id(INT)와 name(VARCHAR)가 같은 위치 → 타입 불일치

✅ 해결 예시: CAST 또는 타입 맞추기

SELECT id, name FROM users
UNION ALL
SELECT CAST(name AS UNSIGNED), birth_date FROM people;

💡 실무 팁

상황 해결 전략
컬럼 수가 다름 NULL, DEFAULT, 상수로 컬럼 추가
타입이 다름 CAST(), CONVERT()로 강제 변환
헤더 통일 AS alias로 컬럼 이름 맞추기
정렬 기준 컬럼 정하기 UNION ALL 결과를 서브쿼리로 감싸서 ORDER BY 적용

✅ 실전 예: 두 테이블 통합

-- 고객 문의 기록 + 이메일 구독 기록 통합 조회
SELECT user_id, created_at, '문의' AS type FROM inquiries
UNION ALL
SELECT NULL AS user_id, subscribed_at AS created_at, '구독' AS type FROM subscribers;
  • 컬럼 수: 3개로 맞춤
  • 타입: user_id는 NULL 가능, 날짜 형식 통일
  • 정렬도 가능

댓글