BAD: M00001 회원, 주문 56건·응모 56건(중복 집계)
M:1:M 조인 후 집계
SELECT m.mbr_id
,COUNT(o.ord_no) AS ord_cnt
,COUNT(e.entry_no) AS entry_cnt
FROM startdbpg.ms_mbr_big m
LEFT OUTER JOIN startdbpg.tr_ord_big o
ON (o.mbr_id = m.mbr_id
AND o.ord_dtm >= '2024-01-01'
AND o.ord_dtm < '2024-02-01')
LEFT OUTER JOIN startdbpg.tr_event_entry_big e
ON (e.mbr_id = m.mbr_id
AND e.entry_dtm >= '2024-01-01'
AND e.entry_dtm < '2024-02-01')
GROUP BY m.mbr_id
ORDER BY m.mbr_id
;
SQL
복사
실행 결과 보기
•
회원 한 명에 주문(여러 건)과 응모(여러 건)를 함께 붙이면 행이 곱해집니다. M00001 회원은 주문 8건 각각에 응모 7건이 모두 붙어 8 × 7 = 56행이 되고, 두 건수가 모두 56으로 나옵니다.
•
M00002처럼 주문·응모가 1건씩이면 결과가 같아 보여서 테스트에서 놓치기 쉽습니다.
GOOD: M00001 회원, 주문 8건·응모 7건(정상)
각 집합을 먼저 집계한 뒤 조인
SELECT m.mbr_id
,COALESCE(o.ord_cnt, 0) AS ord_cnt
,COALESCE(e.entry_cnt, 0) AS entry_cnt
FROM startdbpg.ms_mbr_big m
LEFT OUTER JOIN
(
SELECT o1.mbr_id
,COUNT(*) AS ord_cnt
FROM startdbpg.tr_ord_big o1
WHERE o1.ord_dtm >= '2024-01-01'
AND o1.ord_dtm < '2024-02-01'
GROUP BY o1.mbr_id
) o ON o.mbr_id = m.mbr_id
LEFT OUTER JOIN
(
SELECT e1.mbr_id
,COUNT(*) AS entry_cnt
FROM startdbpg.tr_event_entry_big e1
WHERE e1.entry_dtm >= '2024-01-01'
AND e1.entry_dtm < '2024-02-01'
GROUP BY e1.mbr_id
) e ON e.mbr_id = m.mbr_id
ORDER BY m.mbr_id
;
SQL
복사
실행 결과 보기
데이터 검증 SQL
SELECT 'ord' AS div ,o.mbr_id ,COUNT(*) AS cnt
FROM startdbpg.tr_ord_big o
WHERE o.ord_dtm >= '2024-01-01'
AND o.ord_dtm < '2024-02-01'
AND o.mbr_id = 'M00001'
GROUP BY o.mbr_id
UNION ALL
SELECT 'event' AS div ,e.mbr_id ,COUNT(*) AS cnt
FROM startdbpg.tr_event_entry_big e
WHERE e.entry_dtm >= '2024-01-01'
AND e.entry_dtm < '2024-02-01'
AND e.mbr_id = 'M00001'
GROUP BY e.mbr_id
;
div | mbr_id | cnt
------+--------+----
ord | M00001 | 8
event | M00001 | 7
SQL
복사
•
집계 기준이 회원이므로, 주문과 응모를 각각 회원 단위로 먼저 집계해 회원 1명당 1건으로 만듭니다.
•
그러면 회원(1) : 주문 집계(1) : 응모 집계(1)의 1:1:1 조인이 되어 행이 곱해지지 않습니다.
•
인라인 뷰마다 기간 조건을 넣어 1월 데이터만 집계합니다. 주문이나 응모가 없는 회원(M00004)은 NULL이 되므로 COALESCE로 0을 표시합니다.
관계 구조
erDiagram
ms_mbr_big ||--o{ tr_ord_big : mbr_id
ms_mbr_big ||--o{ tr_event_entry_big : mbr_id
ms_mbr_big {
varchar mbr_id PK
}
tr_ord_big {
numeric ord_no PK
varchar mbr_id
}
tr_event_entry_big {
varchar event_id PK
integer entry_no PK
varchar mbr_id
}Mermaid
복사
KEY POINT
•
한 테이블에 여러 상세 테이블을 함께 조인하면 행이 곱해집니다. (주문 8 × 응모 7 = 56)
•
곱해진 상태에서 COUNT·SUM을 하면 건수와 금액이 부풀려집니다.
•
상세 테이블마다 집계 기준에 맞춰 먼저 집계해 1건으로 만든 뒤 조인합니다.
TIP. DISTINCT로 맞추면 안 되나?
•
COUNT(DISTINCT ord_no)로 건수는 맞출 수 있지만, 늘어난 행은 그대로라 느립니다.
•
SUM(DISTINCT 금액)은 쓰면 안 됩니다. 5,000원 주문이 두 건이면 하나로 합쳐져 5,000원이 됩니다.
•
모든 조합(주문 × 응모)이 실제로 필요한 업무라면 M:1:M 조인 자체는 문제가 아닙니다.
DEEP DIVE


