Search

[권장:성능]서로 다른 테이블의 OR 조건

BAD: 16999.476ms, hit=14590, read=337219

서로 다른 테이블의 OR 조건
SELECT o.ord_no ,o.ord_dtm ,m.mbr_gd FROM startdbpg.tr_ord_big o INNER JOIN startdbpg.ms_mbr_big m ON m.mbr_id = o.mbr_id WHERE 1 = 1 AND ( (o.ord_dtm >= '2024-01-01' AND o.ord_dtm < '2024-01-02') OR (m.mbr_gd = 'PLAT' AND m.nick_nm LIKE 'Ice%') ) ;
SQL
복사
실행계획 보기
•
OR가 서로 다른 테이블에 걸쳐 있어서, DB는 어느 테이블에서도 조건으로 범위를 줄이지 못합니다.
•
주문 2,600만 건 전체를 회원과 조인한 뒤 조건을 검사해서, 2,626만 건을 버리고 26,009건만 남겼습니다(Rows Removed by Join Filter).

GOOD: 102.424ms, hit=7646

조건을 분리하여 검색
SELECT o.ord_no ,o.ord_dtm ,m.mbr_gd FROM startdbpg.tr_ord_big o INNER JOIN startdbpg.ms_mbr_big m ON m.mbr_id = o.mbr_id WHERE 1 = 1 AND (o.ord_dtm >= '2024-01-01' AND o.ord_dtm < '2024-01-02') AND NOT EXISTS( SELECT * FROM startdbpg.ms_mbr_big x WHERE x.mbr_id = o.mbr_id AND x.mbr_gd = 'PLAT' AND x.nick_nm LIKE 'Ice%' ) UNION ALL SELECT o.ord_no ,o.ord_dtm ,m.mbr_gd FROM startdbpg.ms_mbr_big m INNER JOIN startdbpg.tr_ord_big o ON m.mbr_id = o.mbr_id WHERE 1 = 1 AND (m.mbr_gd = 'PLAT' AND m.nick_nm LIKE 'Ice%') ;
SQL
복사
실행계획 보기
•
① 2024-01-01 하루 주문과 ② 'PLAT·Ice' 회원의 주문으로 나눠, 각자 인덱스로 범위를 줄입니다.
◦
① 하루 주문 21,708건을 날짜 인덱스로 바로 찾습니다.
◦
② PLAT·Ice 회원 10명을 먼저 찾고, 그 회원들의 주문 4,302건만 회원 인덱스로 찾습니다.
•
②에는 날짜 조건이 없습니다. 원래 조건이 OR라, PLAT·Ice 회원의 주문은 날짜와 상관없이 모두 포함되어야 하기 때문입니다.
•
2024-01-01에 주문한 PLAT·Ice 회원은 ①과 ② 양쪽에 다 나옵니다. 그래서 ①에서 NOT EXISTS로 그 회원을 빼서 중복을 막습니다(이 예제에서는 1건).
💡

KEY POINT

•
서로 다른 테이블의 조건을 OR로 묶으면, 어느 쪽 조건으로도 범위를 줄이지 못해 전체를 조인한 뒤 걸러냅니다.
•
이럴 때는 조건별로 SQL을 나눠 UNION ALL로 합칩니다.
•
나눌 때는 두 결과에 같은 행이 겹치지 않도록, 한쪽에서 다른 쪽 조건을 제외합니다.
✏️

TIP. UNION으로 쓰면 안 되나?

•
UNION으로 합쳐도 중복은 제거되지만, 결과 전체를 비교하는 비용이 추가로 듭니다.
•
겹치지 않게 나눌 수 있다면 UNION ALL이 낫습니다.
📖 DEEP DIVE — SQL STARTER for MySQL