Skip to content
튜닝 연습옵티마이저와 통계
OPT-023

정산팀 카드 결제 대사 화면이 주문상세를 주문 수만큼 1,370번 찾아간다

기초SELECT한빛유통613 / 700

상황 ​

정산팀의 「카드 결제 대사」는 월을 고르면 그 달에 주문하고 그 달에 결제된 카드 주문을 주문별 상세 금액 합계와 함께 보여 준다. PG사 정산서와 맞춰 보는 화면이라 마감 주간에는 정산팀 12명이 하루 수백 번 누른다. 화면은 월을 리터럴로 넣는다(아래는 2024년 6월). 개발자의 설명은 이렇다. "주문일도 결제일도 그 달이어야 해서 조건을 둘 다 걸었어요."

테이블과 인덱스 ​

  • T_ORD: 100,000행 · 896블록 · PK_T_ORD (ORD_SEQ) · ORD_DT DATE(시분초 있음, 2023-01-01 ~ 2024-12-31, 하루 약 137건) · PAY_DT DATE(결제일 0시) · PAY_TP CARD · CASH · POINT가 1/3씩
  • 이 테이블의 결제는 모두 주문 당일에 일어났다(PAY_DT = TRUNC(ORD_DT)). 결제일이 주문일보다 앞선 행은 없다.
  • 2024년 6월 주문 4,110건 · 그중 카드 결제 1,370건
  • T_ORD_DET: 200,000행 · 896블록 · PK_T_ORD_DET (ORD_SEQ, ORD_DET_NO): BLEVEL 1 · 주문당 1 ~ 3건(평균 2건)
  • 통계 최신 · 히스토그램 없음 · 확장 통계 없음 · ORD_DT NUM_DISTINCT 약 10만(시분초) · PAY_DT NUM_DISTINCT 730

원본 SQL ​

sql
SELECT o.ORD_SEQ, o.ORD_DT, o.PAY_DT, o.PAY_AMT, SUM(d.ORD_QTY * d.UNT_PRC) AS det_amt
  FROM T_ORD o, T_ORD_DET d
 WHERE o.ORD_DT >= DATE '2024-06-01' AND o.ORD_DT < DATE '2024-07-01'
   AND o.PAY_DT >= DATE '2024-06-01' AND o.PAY_DT < DATE '2024-07-01'
   AND o.PAY_TP = 'CARD'
   AND d.ORD_SEQ = o.ORD_SEQ
 GROUP BY o.ORD_SEQ, o.ORD_DT, o.PAY_DT, o.PAY_AMT

실행계획 학습용 예시 ​

xplan
--------------------------------------------------------------------------------------------------
| Id  | Operation                    | Name         | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
--------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |              |      1 |        |   1370 |00:00:00.03 |    5006 |
|   1 |  HASH GROUP BY               |              |      1 |     58 |   1370 |00:00:00.03 |    5006 |
|   2 |   NESTED LOOPS               |              |      1 |    116 |   2740 |00:00:00.03 |    5006 |
|   3 |    NESTED LOOPS              |              |      1 |    116 |   2740 |00:00:00.02 |    3636 |
|*  4 |     TABLE ACCESS FULL        | T_ORD        |      1 |     58 |   1370 |00:00:00.01 |     896 |
|*  5 |     INDEX RANGE SCAN         | PK_T_ORD_DET |   1370 |      2 |   2740 |00:00:00.01 |    2740 |
|   6 |    TABLE ACCESS BY INDEX ROWID| T_ORD_DET   |   2740 |      1 |   2740 |00:00:00.01 |    1370 |
--------------------------------------------------------------------------------------------------
   4 - filter(("O"."PAY_TP"='CARD' AND
              "O"."ORD_DT">=TO_DATE(' 2024-06-01 00:00:00', 'syyyy-mm-dd hh24:mi:ss') AND
              "O"."ORD_DT"<TO_DATE(' 2024-07-01 00:00:00', 'syyyy-mm-dd hh24:mi:ss') AND
              "O"."PAY_DT">=TO_DATE(' 2024-06-01 00:00:00', 'syyyy-mm-dd hh24:mi:ss') AND
              "O"."PAY_DT"<TO_DATE(' 2024-07-01 00:00:00', 'syyyy-mm-dd hh24:mi:ss')))
   5 - access("D"."ORD_SEQ"="O"."ORD_SEQ")

문제 ​

  1. 4번 줄의 E-Rows 58을 손으로 계산하고, 실제 1,370과 왜 다른지 데이터로 설명하시오. 그 58 때문에 무엇을 골랐는지도 쓰시오.
  2. 결과가 바뀌지 않게 SQL을 고쳐 추정을 바로잡으시오. 두 열에 컬럼 그룹 통계를 만들면 고쳐지는지도 쓰시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자