핵심 인사이트 (3줄 요약)
- 본질: 집합 연산자(Set Operator)는 2개 이상의 독립적인
SELECT쿼리 결과물(Result Set)들을 위아래로 이어 붙여 **합집합(UNION), 교집합(INTERSECT), 차집합(MINUS/EXCEPT)**이라는 수학적 벤 다이어그램 형태의 1개의 거대한 테이블 덩어리로 뽑아내는 뼈대 연산자다.- 가치: 서로 조인(Join)할 연결 고리(PK/FK)가 아예 없는 남남인 두 테이블(예: 작년 은퇴한 사원 테이블과 올해 신입 사원 테이블)의 명단을 한 화면에 세로로 쭉 이어서 통합 대시보드로 뽑아내야 할 때 유일무이한 구원 투수 역할을 한다.
- 판단 포인트:
UNION은 두 덩어리를 합칠 때 중복 데이터를 지우기 위해 미친듯한 정렬(Sort) 메모리 연산을 유발하여 DB를 박살 낼 위험이 크다. 따라서 실무 DBA는 중복을 허용하더라도 무조건 갖다 붙이는 **UNION ALL로 튜닝하여 I/O 병목을 0으로 만드는 아키텍처적 타협(Trade-off)**을 필수적으로 구사한다.
Ⅰ. 개요 및 필요성
집합 연산자(Set Operator)는 관계 대수(Relational Algebra)의 가장 기본이 되는 수학적 집합 연산을 SQL 문법으로 구현한 것이다. 여러 개의 독립적인 SELECT 질의 결과를 하나의 결과 집합(Single Result Set)으로 통합(세로 확장)해 준다.
회사에 '정규직 사원' 테이블 1만 명과, '계약직 사원' 테이블 5천 명이 물리적으로 분리되어 존재한다. 사장님이 "우리 회사 다니는 1만 5천 명 전체 명단 이름이랑 전화번호 좀 쭉 다 뽑아와!"라고 지시했다. 초보 개발자가 당황한다. 두 테이블을 엮어보려고 조인(Join)을 걸었더니 가로로 뚱뚱해지거나 카테시안 곱(뻥튀기)이 나버린다. "이건 엮는(가로) 게 아니라, A 표 결과물 밑에 B 표 결과물을 엑셀 복붙(Ctrl+C, Ctrl+V) 하듯 그냥 '세로'로 무식하게 냅다 덧붙이고 싶어!"라는 데이터 결합의 가장 원초적인 갈증이 집합 연산자를 탄생시켰다.
- 📢 섹션 요약 비유: **조인(Join)**이 남자 명부와 여자 명부에서 커플 매칭이 되는 사람끼리 손을 잡게 해서 1줄짜리 가로(옆으로 긴) 명단을 만드는 **'소개팅 매칭'**이라면, **집합 연산자(UNION)**는 남자 명부 100장 밑에 풀을 칠해서 여자 명부 100장을 그냥 세로로 길게 척 가져다 붙여 총 200장짜리 길쭉한 **'전체 인원 출석부 1장'**을 만들어버리는 투박하지만 확실한 문서 병합기입니다.
Ⅱ. 아키텍처 및 핵심 원리
수학적 벤 다이어그램과 SQL 집합 연산자의 매핑 구조다.
┌─────────────────────────────────────────────────────────────┐
│ 수학적 벤 다이어그램과 SQL 집합 연산자 매핑 맵 (Map) │
├─────────────────────────────────────────────────────────────┤
│ │
│ [ 테이블 A (수학 90점 이상) ] [ 테이블 B (영어 90점 이상) ] │
│ ( 홍길동, 이순신 ) ( 홍길동, 강감찬 ) │
│ │
│ 1️⃣ UNION (합집합 ∪) ➔ "수학이든 영어든 하나라도 90점 넘는 애 싹 다 와!"│
│ 결과: (홍길동, 이순신, 강감찬) │
│ 🌟 특징: 홍길동이 2명이지 않고 1명으로 '중복 제거(Sort Unique)' 됨! │
│ │
│ 2️⃣ UNION ALL (무지성 합집합) ➔ "중복 신경 안 써! 그냥 다 쏟아부어!" │
│ 결과: (홍길동, 이순신, 홍길동, 강감찬) │
│ 🌟 특징: 홍길동이 2번 나옴. 하지만 정렬(Sort) 연산을 안 해서 압도적으로 빠름!│
│ │
│ 3️⃣ INTERSECT (교집합 ∩) ➔ "수학, 영어 둘 다 90점 넘는 완벽한 천재만!" │
│ 결과: (홍길동) │
│ │
│ 4️⃣ MINUS / EXCEPT (차집합 -) ➔ "수학 90점 넘는 애 중에서, 영어 잘하는 애 빼!"│
│ 결과 (A MINUS B): (이순신) │
│ 결과 (B MINUS A): (강감찬) ◀─ 순서 바뀌면 결과도 완전 달라짐! │
└─────────────────────────────────────────────────────────────┘
[집합 연산자의 성립 전제 조건 (Rule of Thumb)]
아무 쿼리나 냅다 UNION으로 붙일 수 없다. 마치 엑셀 2장을 복붙할 때 표의 열(Column) 개수와 모양이 딱 맞아야 안 깨지는 것과 같다.
- 컬럼의 개수 일치: 위쪽
SELECT가 3개 컬럼을 뽑았으면, 아래쪽SELECT도 무조건 3개여야 한다. 위는 이름, 나이 2개인데 아래는 이름 1개면 붙이다 에러 뿜는다. - 데이터 타입(Type)의 호환성: 위쪽 첫 번째 컬럼이 숫자형(Number)이면, 아래쪽 첫 번째 컬럼도 무조건 숫자형이어야 세로로 꿰어 맞출 수 있다. 위에 숫자를 넣고 밑에 문자(Varchar)를 끼우면 파서(Parser)가 피를 토한다.
- ORDER BY는 맨 마지막에 딱 1번만: 집합 연산자로 다 이어 붙이기도 전에, 윗동네 쿼리에서 지 혼자
ORDER BY로 줄을 서는 건 문법적 사형 선고다. 덩어리들을 다 뭉친 뒤(합집합 끝난 뒤) 맨 끄트머리에 딱 한 줄만ORDER BY를 써서 전체 통제(Global Sort)를 해야 한다.
- 📢 섹션 요약 비유: 두 개의 레고 상자를 합칠(UNION) 때는 무조건 위 블록과 아래 블록의 '튀어나온 핀 구멍 개수(컬럼 개수)'가 똑같아야만 딸깍 하고 결합할 수 있습니다. 위는 4칸인데 아래는 3칸짜리 블록을 가져와서 억지로 누르면 찌그러져서 튕겨(에러) 나옵니다.
Ⅲ. 비교 및 연결
개발자들의 가장 흔한 실수이자 배치 서버를 뻗게 만드는 영원한 딜레마다. UNION과 UNION ALL의 차이다.
| 비교 항목 | UNION (순수 합집합) | UNION ALL (단순 이어 붙이기) | 튜닝 아키텍트의 타협안 |
|---|---|---|---|
| 작동 원리 | 두 덩어리를 합친 뒤 ➔ 숨어있던 DISTINCT(중복 제거) 엔진 강제 발동 ➔ 고유한 데이터만 남김 | 엔진 개입 없음. 그냥 위 덩어리 뱉고 나서, 바로 밑에 덩어리 붙여서 쭉 뱉어냄 (Bypass). | 실무에서 두 쿼리의 출처가 완전히 달라 애초에 교집합(중복)이 발생할 확률이 0%라면? 무조건 UNION ALL을 써야 한다! |
| DB 메모리 부하 | 중복을 솎아내기 위해 수억 건을 **PGA(Sort Area)에 부어놓고 전체 정렬(Sort Unique)**을 때림. 💥 | 정렬 따윈 안 함. 들어온 대로 물 흐르듯 화면에 뿌림 (Streaming). | - |
| I/O 병목 | 메모리가 부족하면 디스크의 임시(Temp) 스페이스에 쏟아내며 쓰기/읽기 폭주. 야간 배치 실패 1순위. | 가장 가볍고 디스크 스와핑(Thrashing)이 전혀 없음. 속도의 제왕. | 중복 제거가 진짜 꼭 필요하다면, UNION ALL로 일단 뭉친 뒤, 바깥에서 ROW_NUMBER() 윈도우 함수를 써서 1등(최신) 데이터만 우아하게 발라내는(Cleansing) 튜닝으로 우회한다. |
수학의 집합(Set)은 본질적으로 중복 원소를 허용하지 않는다. 따라서 UNION은 두 덩어리를 합칠 때 똑같은 홍길동이 있으면 1명을 깎아낸다. 이 깎아내는 작업(Sort Unique)은 DB 엔진에게 수만 건의 데이터를 램(RAM)에 부어 넣고 퀵 소트(Quick Sort)를 치게 만드는 극악의 메모리 병목을 일으킨다. 그래서 DB 튜너들은 죽이 되든 밥이 되든 일단 빠르고 가벼운 UNION ALL을 쳐놓고, 정 중복이 거슬리면 바깥쪽 껍데기(인라인 뷰)에서 DISTINCT나 윈도우 함수로 우아하게 요리하는 아키텍처를 선호한다.
- 📢 섹션 요약 비유:
UNION ALL은 쓰레기통 2개를 그냥 커다란 봉투 1개에 우당탕탕 쏟아붓는 겁니다. 1초면 끝납니다.UNION은 쓰레기통 2개를 바닥에 다 펼쳐놓고 똑같이 생긴 쓰레기(중복)가 있는지 눈이 빠지게 검사해서 버린 다음에야 큰 봉투에 담는 겁니다. 데이터가 1억 건이면 검사하다가 날밤을 새우고 서버가 쓰러집니다.
Ⅳ. 실무 적용 및 기술사 판단
집합 연산자의 진짜 활용법은 가짜 컬럼을 추가해 이종 데이터를 포매팅하는 것이다.
실무 판단 시나리오
- 구시대 레거시 분할 테이블의 강제 통합 통계 리포트: 회사의 DB가 너무 낡아
주문_2024테이블과주문_2025테이블을 년도별로 찢어서 따로 만들어 두었다(수동 파티셔닝). 사장님이 "최근 2년 치 총주문 건수와 매출 총합을 한 줄로 뽑아와!"라고 시켰다.- 판단: 집합 연산자의 가장 고전적이고 아름다운 실무 활용처다. 아키텍트는 두 테이블을 인라인 뷰(Inline View 괄호) 안에서
UNION ALL로 먼저 길게 위아래로 붙여서 하나의 거대한 가상 테이블(가짜 2년 치 덩어리)을 창조한다.SELECT SUM(매출) FROM ( SELECT 매출 FROM 주문_2024 UNION ALL SELECT 매출 FROM 주문_2025 );그리고 그 가상 테이블 바깥에서SUM이라는 믹서기(집계 함수)를 한 방 윙~ 갈아버리면, 물리적으로 찢어진 두 우주의 데이터가 하나의 완벽한 통계 숫자로 0.1초 만에 환생한다.
- 판단: 집합 연산자의 가장 고전적이고 아름다운 실무 활용처다. 아키텍트는 두 테이블을 인라인 뷰(Inline View 괄호) 안에서
UNION과 다중OR조회의 인덱스 튜닝 마개조: "A부서 직원(10명)이거나, 급여가 1억 넘는 직원(5명) 명단을 뽑아라." 주니어 개발자가SELECT * FROM 사원 WHERE 부서='A' OR 급여>1억;으로 짰다.OR연산자 특성상 옵티마이저는 부서 인덱스와 급여 인덱스를 타지 못하고 혼란에 빠져 1,000만 건 사원 테이블을 무식하게 풀스캔(Full Scan) 때리며 5분이 걸렸다.- 판단: SQL 튜너들의 영원한 무기, 'OR 조건을 UNION으로 찢어발기기' 비기다.
SELECT * FROM 사원 WHERE 부서='A'(부서 인덱스 광속 탑승 0.01초!)UNION(이때는 두 조건에 모두 맞는 교집합 직원이 있을 수 있으니 중복 제거를 위해 UNION을 쓴다)SELECT * FROM 사원 WHERE 급여>1억;(급여 인덱스 광속 탑승 0.01초!) 풀스캔 돌며 5분 걸리던 멍청한OR쿼리가, 인덱스를 각자 찰떡같이 타는 빠른 쿼리 2방과UNION조합으로 마개조되며 0.1초 만에 결과가 화면에 꽂힌다. 옵티마이저의 약점(OR)을 찌르는 완벽한 아키텍처 수술이다.
- 판단: SQL 튜너들의 영원한 무기, 'OR 조건을 UNION으로 찢어발기기' 비기다.
안티패턴
-
IN서브쿼리를INTERSECT로 착각하고 바꾸기: "A 부서이면서 동시에 B 동호회에 속한 놈을 찾아라(교집합)"라고 할 때, 쿼리를SELECT * FROM A INTERSECT SELECT * FROM B로 멋부려 짜는 짓.INTERSECT역시UNION처럼 내부적으로 숨 막히는 중복 제거 및 정렬(Sort) 연산을 수반한다. 똑똑한 튜너는 집합 연산자를 버리고, 조인(Join)이나WHERE EXISTS (SELECT 1 FROM B...)라는 세미 조인(Semi-Join)으로 우회하여 옵티마이저가 해시 맵으로 부드럽게 필터링하고 빠져나가도록 유도한다. 집합 연산자는 최후의 보루여야 한다. -
📢 섹션 요약 비유: 빵과 소시지(서로 다른 모양의 테이블)를 어떻게든 한 줄로 꼬챙이에 꿰어야(UNION) 하는데 모양이 안 맞습니다. 이때 "가짜 투명 젤리(NULL)"를 군데군데 끼워 넣어서 전체 모양(컬럼 규격)을 네모나게 맞춰버리는 천재적인 꼼수가 더미(Dummy) 컬럼 융합입니다. 이렇게 하면 에러 없이 완벽한 바비큐 꼬치가 완성됩니다.
Ⅴ. 기대효과 및 결론
집합 연산자(Set Operators)는 관계 대수(Relational Algebra)의 가장 원초적이면서도 직관적인 데이터 결합 도구다.
UNION ALL은 데이터를 세로로 무한정 확장할 수 있는 길을 열어, 과거 분산 파티션 뷰(Partition View)의 탄생을 이끌었고, 빅데이터 하둡(Hadoop) 에코시스템에서 각 워커 노드가 연산한 파편들을 마스터 노드가 1개의 화면으로 긁어모으는(Reduce) 심장 뼈대로 활약하고 있다. 현대 프론트엔드 앱의 '무한 스크롤(Infinite Scroll)' 기능 역시 백엔드가 20개씩 끊어주는 데이터를 프론트엔드 배열에 UNION ALL로 밑장 빼기 하듯 이어 붙이는 철학의 연장선이다.
가로(Join)로 엮이지 못하는 운명이라도, 세로(Union)로 쌓아 올려 거대한 탑을 짓는다. 집합 연산자는 10년 전 은퇴한 선배들의 먼지 쌓인 장부(Archive)와 오늘 아침 입사한 신입의 장부(Live)를 모니터 화면 위에서 단 하나의 완벽한 '전체 명단(Single View)'으로 부활시키는 마법의 보자기다. 이 무식하지만 파괴적인 세로의 확장을 이해하고, UNION의 정렬(Sort) 독가스를 피하는 통제력을 얻었을 때 아키텍트는 비로소 데이터의 물리적 파편화를 완벽히 극복하게 된다.
- 📢 섹션 요약 비유: 조인(Join)은 햄버거 빵과 고기 패티를 위아래로 포개어 **'새로운 요리 1개(가로 확장)'**를 완성하는 우아한 요리법입니다. 집합 연산자(UNION)는 햄버거 상자 10개가 든 박스 밑에 햄버거 상자 10개를 겹쳐서 쌓아 올리고 테이프를 감아 **'거대한 20개짜리 탑(세로 확장)'**을 만들어 납품하는 무식하지만 확실한 화물 상하차 포장술입니다.
📌 관련 개념 맵
| 개념 | 연결 포인트 |
|---|---|
| UNION ALL (무지성 합집합) | UNION의 착한 동생. 중복을 검사하느라 서버 램을 태워 먹는 형(UNION)과 달리, 눈 딱 감고 들어오는 족족 화면에 밑장 빼기로 붙여버려 극강의 스피드를 자랑하는 빛의 연산자다. |
| 조인 (Join) | 집합 연산자가 데이터를 '세로'로 무식하게 쌓는다면, 조인은 사원 번호라는 끈(PK/FK)을 매개로 '가로'로 우아하게 엮어서 뚱뚱한 정보를 만들어 내는 관계 대수의 또 다른 심장이다. |
| Sort Area (정렬 메모리) | UNION, INTERSECT, MINUS 3형제가 중복을 깎아내기 위해 1억 건의 데이터를 부어놓고 미친 듯이 퀵 소트(Quick Sort)를 갈겨대는 DB 내부의 뜨거운 화약고 메모리 구역이다. |
| 파티션 뷰 (Partition View) | 낡은 RDBMS에서 월별로 12개의 테이블을 쪼개놓고, 겉에 뷰(View) 껍데기를 하나 씌운 뒤 그 안에서 12개 테이블을 UNION ALL로 이어 붙여 마치 거대한 1개의 테이블인 척 뇌를 속이는 꼼수 아키텍처다. |
| SQL 인젝션 (SQL Injection) | 해커가 로그인 아이디 칸에 ' UNION SELECT 비번 FROM 관리자 -- 라고 쳐서, 원래 쿼리 밑에 자기 해킹 쿼리를 세로로 슬쩍 덧붙여 화면에 띄워 훔쳐 가는 최악의 보안 뚫림 취약점이다. |
📈 관련 키워드 및 발전 흐름도
수학적 집합론(Set Theory) / 벤 다이어그램의 합집합, 교집합, 차집합 개념
│
▼
관계 대수(Relational Algebra) / E.F. Codd가 데이터베이스 릴레이션 조작을 위해 수학적 연산을 RDBMS에 도입
│
▼
SQL-92 표준 UNION, INTERSECT, MINUS 확립 / 이기종 테이블의 세로 결합(Vertical Merge) 쿼리 문법 완성
│
▼
UNION 정렬(Sort) 병목 파국 💥 / 수억 건 중복 검사로 야간 배치 서버 셧다운 ➔ 튜너들의 UNION ALL 바이패스 스트리밍 튜닝 마개조 ✨
│
▼
빅데이터 및 스트리밍 아키텍처 / HDFS 리듀스 병합 및 Kafka 실시간 데이터 스트림의 무한 세로 덧붙이기(Continuous Union All) 엔진으로 철학 승화
👶 어린이를 위한 3줄 비유 설명
- 1반 출석부(30명)랑 2반 출석부(30명)가 따로 종이에 적혀있어요. 이걸 하나로 합치고 싶어요!
- **조인(Join)**은 1반 철수 옆에 2반 철수 짝꿍을 가로로 묶어서 표를 옆으로 길게(뚱뚱하게) 만드는 거라면, **UNION(집합 연산자)**은 1반 출석부 밑에 풀을 칠해서 2반 출석부를 세로로 길쭉하게 이어 붙여 총 60명짜리 거대한 '전체 명단'을 만드는 무식하지만 확실한 풀칠 마법이에요!
- 여기서 UNION ALL은 실수로 이름이 2번 중복해서 적힌 애가 있어도 그냥 막 넘어가서 엄청 빠른 풀칠이고, UNION은 똑같은 이름이 2번 나오면 지우개로 1번을 빡빡 지우느라 시간이 엄청 오래 걸리는 깐깐한 풀칠이랍니다!