두 테이블의 조회 결과를 합칠 때 UNION과 UNION ALL을 사용할 수 있습니다. 선택 기준은 단순히 “중복이 있느냐”가 아니라 같은 값의 행이 서로 다른 기록을 뜻할 수 있느냐입니다.
예제는 SQLite에서 실행했습니다. 온라인과 오프라인에서 발생한 구매 금액을 합치는 상황입니다.
CREATE TABLE online_sales (amount INTEGER NOT NULL);
CREATE TABLE offline_sales (amount INTEGER NOT NULL);
INSERT INTO online_sales VALUES (1000), (2000);
INSERT INTO offline_sales VALUES (1000), (3000);
1000원 구매가 양쪽에 한 건씩 있습니다. 금액은 같아도 총 네 번의 구매입니다.
SELECT amount FROM online_sales
UNION ALL
SELECT amount FROM offline_sales
ORDER BY amount;
결과는 1000, 1000, 2000, 3000입니다. UNION ALL은 각 조회 결과의 행을 중복 제거 없이 합칩니다.
SELECT amount FROM online_sales
UNION
SELECT amount FROM offline_sales
ORDER BY amount;
이번 결과는 1000, 2000, 3000입니다. 조회한 열이 amount 하나이므로 같은 금액의 두 행이 하나로 합쳐졌습니다.
두 열을 조회했다면 두 열의 값이 모두 같은 행이 중복 제거 대상입니다. UNION이 실제 구매의 동일성을 판단하거나 기본키를 알아서 찾아 비교하는 것은 아닙니다.
| 연산 | 결과 행 수 | 금액 합계 |
|---|---|---|
| UNION ALL | 4 | 7000 |
| UNION | 3 | 6000 |
서로 다른 구매를 합산하려는 상황에서 UNION을 쓰면 실제 금액보다 적게 집계될 수 있습니다.
WITH all_sales AS (
SELECT amount FROM online_sales
UNION ALL
SELECT amount FROM offline_sales
)
SELECT COUNT(*) AS sale_count, SUM(amount) AS total_amount
FROM all_sales;
결과는 sale_count = 4, total_amount = 7000입니다. 반대로 “구매에 사용된 서로 다른 금액의 목록”이 목적이라면 UNION이 적절할 수 있습니다.
두 SELECT는 열 개수가 같아야 하고, 같은 위치의 열끼리 의미와 자료형을 맞춰야 합니다. 이 예제는 모두 정수 금액으로 통일했습니다. 결과의 순서가 필요하면 전체 쿼리 끝에 ORDER BY를 명시합니다.
성능만 보고 무조건 하나를 선택하기보다, 결과의 한 행이 무엇을 의미해야 하는지부터 정하는 것이 우선입니다. 중복 제거가 불필요한 거래 내역에는 UNION ALL을, 서로 다른 값의 목록이 필요할 때는 UNION을 검토할 수 있습니다.