Search

REMINDER 03-08. 업무상 필수인 조회 조건을 빠뜨리지 말자

상황: 회원 M0019의 2023년 3월 주문을 조회하는 화면을 구현한다. 원래 예제의 M00019는 실제 startdbpg.tr_ord에 존재하지 않아, 실데이터에 존재하는 M0019로 변경했다. tr_ord에는 tr_ord_fk01(mbr_id), tr_ord_x51(ord_dtm) 인덱스가 이미 있다.

BAD SQL — 회원 조건을 빠뜨리고 3월 주문 전체 검색

EXPLAIN (ANALYZE, BUFFERS, COSTS OFF) SELECT o.ord_no, o.mbr_id, o.ord_dtm FROM startdbpg.tr_ord o WHERE o.ord_dtm >= TIMESTAMP '2023-03-01' AND o.ord_dtm < TIMESTAMP '2023-04-01';
SQL
복사
실측 실행계획 (BAD, 17,582건)
Index Scan using tr_ord_x51 on tr_ord o (actual time=0.052..5.920 rows=17582 loops=1) Index Cond: ((ord_dtm >= '2023-03-01 00:00:00'::timestamp without time zone) AND (ord_dtm < '2023-04-01 00:00:00'::timestamp without time zone)) Buffers: shared hit=262 Planning: Buffers: shared hit=139 Planning Time: 0.750 ms Execution Time: 7.129 ms
Plain Text
복사

GOOD SQL — 반드시 필요한 회원 조건을 함께 지정

EXPLAIN (ANALYZE, BUFFERS, COSTS OFF) SELECT o.ord_no, o.mbr_id, o.ord_dtm FROM startdbpg.tr_ord o WHERE o.mbr_id = 'M0019' AND o.ord_dtm >= TIMESTAMP '2023-03-01' AND o.ord_dtm < TIMESTAMP '2023-04-01';
SQL
복사
실측 실행계획 (GOOD, 31건)
Bitmap Heap Scan on tr_ord o (actual time=5.902..6.044 rows=31 loops=1) Recheck Cond: (((mbr_id)::text = 'M0019'::text) AND (ord_dtm >= '2023-03-01 00:00:00'::timestamp without time zone) AND (ord_dtm < '2023-04-01 00:00:00'::timestamp without time zone)) Heap Blocks: exact=31 Buffers: shared hit=80 -> BitmapAnd (actual time=5.876..5.877 rows=0 loops=1) Buffers: shared hit=49 -> Bitmap Index Scan on tr_ord_fk01 (actual time=4.857..4.857 rows=2130 loops=1) Index Cond: ((mbr_id)::text = 'M0019'::text) Buffers: shared hit=5 -> Bitmap Index Scan on tr_ord_x51 (actual time=0.888..0.888 rows=17582 loops=1) Index Cond: ((ord_dtm >= '2023-03-01 00:00:00'::timestamp without time zone) AND (ord_dtm < '2023-04-01 00:00:00'::timestamp without time zone)) Buffers: shared hit=44 Planning: Buffers: shared hit=150 Planning Time: 0.728 ms Execution Time: 6.857 ms
Plain Text
복사
실측 비교
항목
BAD: 기간만 조회
GOOD: 회원 + 기간
결과 행 수
17,582건
31건
주요 접근
tr_ord_x51 Index Scan
BitmapAnd • Bitmap Heap Scan
인덱스 조건
ord_dtm
mbr_id • ord_dtm
Buffers hit
262
80
실행시간(1회)
7.129ms
6.857ms
전체 3월 주문은 17,582건이며 이 중 M0019의 주문은 31건이다. BAD는 화면 요구와 관계없는 다른 회원의 주문 17,551건까지 반환한다. GOOD은 회원 인덱스에서 2,130건, 날짜 인덱스에서 17,582건의 후보를 각각 찾고, BitmapAnd로 두 조건을 결합한 후 실제 테이블에서 31건을 확인했다(Heap Blocks: exact=31). 즉, GOOD이 처음부터 인덱스 항목 31개만 읽은 것은 아니다.

핵심

•
기간·회원·매장·조직처럼 업무상 반드시 필요한 검색 조건은 WHERE에서 빠뜨리지 말자. 이번에는 인덱스를 추가하지 않았어도 필수 회원 조건을 지정하는 것만으로 조회 범위와 접근 방식이 달라졌다. 조회 건수의 큰 차이가 핵심이며, 단일 실행시간 차이 0.272ms만으로 의미 있는 성능 향상을 단정하지 않는다. 반환되는 결과 건수에 따라 애플리케이션 데이터 전송·처리량에도 차이가 생길 수 있다.
💡
참고·예외
•
BAD와 GOOD은 동일한 결과를 반환하는 동치 SQL이 아니다. 업무 요구가 특정 회원의 주문 조회일 때만 GOOD이 정확하다. 관리자용 전체 회원 주문 조회에는 회원 조건을 임의로 추가하지 않는다. 실제 서비스에서는 회원 ID를 바인드 변수로 전달하고 권한도 별도로 검증해야 한다.