Skip to content
튜닝 연습서브쿼리와 쿼리 변환
SUBQ-058

운영 상황판의 「이번 달 주문 현황」 타일 하나가 논리 읽기 1위에 올랐다

기초SELECT한빛유통293 / 700

상황 ​

운영 상황판 첫 화면의 「이번 달 주문 현황」 타일은 이번 달 전체 주문 수, 완료 주문 수와 금액, 대기 주문 수, 취소 주문 수를 보여 주고, 업무 시간(09 ~ 21시)에는 1분마다 새로 고친다. 한 번에 0.5초라 아무도 신경 쓰지 않았는데, 지난주 AWR의 SQL ordered by Gets에서 이 SQL이 1위로 나왔다(하루 720번, 약 2,100만 블록). 아래는 2024년 6월을 조회했을 때의 실행계획이다.

테이블과 인덱스 ​

  • T_ORD_BIG: 1,000,000행 · 5,826블록 · 이 문제에서는 인덱스가 없다 · ORD_DT DATE(전부 자정) · ORD_ST COMPLETE 90% · PENDING 8% · CANCEL 2%
  • 2024년 6월: 27,390건(COMPLETE 24,651 · PENDING 2,191 · CANCEL 548) · 통계 최신
  • 화면은 이번 달 1일과 다음 달 1일을 DATE 바인드(:v_from, :v_to)로 넘긴다

원본 SQL ​

sql
SELECT (SELECT COUNT(*) FROM T_ORD_BIG
         WHERE ORD_DT >= :v_from AND ORD_DT < :v_to)                         AS all_cnt,
       (SELECT COUNT(*) FROM T_ORD_BIG
         WHERE ORD_DT >= :v_from AND ORD_DT < :v_to AND ORD_ST = 'COMPLETE') AS cmpl_cnt,
       (SELECT SUM(ORD_AMT) FROM T_ORD_BIG
         WHERE ORD_DT >= :v_from AND ORD_DT < :v_to AND ORD_ST = 'COMPLETE') AS cmpl_amt,
       (SELECT COUNT(*) FROM T_ORD_BIG
         WHERE ORD_DT >= :v_from AND ORD_DT < :v_to AND ORD_ST = 'PENDING')  AS pend_cnt,
       (SELECT COUNT(*) FROM T_ORD_BIG
         WHERE ORD_DT >= :v_from AND ORD_DT < :v_to AND ORD_ST = 'CANCEL')   AS cncl_cnt
  FROM DUAL

실행계획 학습용 예시 ​

xplan
---------------------------------------------------------------------------------------------
| Id  | Operation            | Name      | Starts | E-Rows | A-Rows |      A-Time | Buffers |
---------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT     |           |      1 |        |      1 | 00:00:00.52 |   29130 |
|   1 |  SORT AGGREGATE      |           |      1 |      1 |      1 | 00:00:00.11 |    5826 |
|*  2 |   TABLE ACCESS FULL  | T_ORD_BIG |      1 |  27266 |  27390 | 00:00:00.11 |    5826 |
|   3 |  SORT AGGREGATE      |           |      1 |      1 |      1 | 00:00:00.10 |    5826 |
|*  4 |   TABLE ACCESS FULL  | T_ORD_BIG |      1 |  24539 |  24651 | 00:00:00.10 |    5826 |
|   5 |  SORT AGGREGATE      |           |      1 |      1 |      1 | 00:00:00.10 |    5826 |
|*  6 |   TABLE ACCESS FULL  | T_ORD_BIG |      1 |  24539 |  24651 | 00:00:00.10 |    5826 |
|   7 |  SORT AGGREGATE      |           |      1 |      1 |      1 | 00:00:00.10 |    5826 |
|*  8 |   TABLE ACCESS FULL  | T_ORD_BIG |      1 |   2181 |   2191 | 00:00:00.10 |    5826 |
|   9 |  SORT AGGREGATE      |           |      1 |      1 |      1 | 00:00:00.10 |    5826 |
|* 10 |   TABLE ACCESS FULL  | T_ORD_BIG |      1 |    545 |    548 | 00:00:00.10 |    5826 |
|  11 |  FAST DUAL           |           |      1 |      1 |      1 | 00:00:00.01 |       0 |
---------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   2 - filter(("ORD_DT">=:V_FROM AND "ORD_DT"<:V_TO))
   4 - filter(("ORD_ST"='COMPLETE' AND "ORD_DT">=:V_FROM AND "ORD_DT"<:V_TO))
   6 - filter(("ORD_ST"='COMPLETE' AND "ORD_DT">=:V_FROM AND "ORD_DT"<:V_TO))
   8 - filter(("ORD_ST"='PENDING' AND "ORD_DT">=:V_FROM AND "ORD_DT"<:V_TO))
  10 - filter(("ORD_ST"='CANCEL' AND "ORD_DT">=:V_FROM AND "ORD_DT"<:V_TO))

문제 ​

  1. 이 SQL 한 번이 몇 블록을 읽는지, 그 블록이 어디서 나오는지 실행계획의 줄 번호로 설명하시오.
  2. 결과 다섯 칸이 바뀌지 않게 T_ORD_BIG을 한 번만 읽도록 SQL을 고치시오. 건수를 셀 때 CASE의 ELSE를 어떻게 써야 하는지도 설명하시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자