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

고객 한 명의 주문 50건을 보려고 주문상세 10만 건을 다 묶는다

중급SELECT한빛유통240 / 700

상황 ​

콜센터의 「고객 주문 내역」 화면은 상담원 40명이 통화할 때마다 고객번호로 연다. 하루 약 6,000번 열리는데, 주문이 50건뿐인 고객도 0.1초씩 걸린다. 인라인 뷰 안의 한 줄에는 -- 뷰를 먼저 만들게 (2019.03 강도현)라는 주석이 달려 있다. 그해 3월 뷰가 풀리면서 계획이 흔들린 일이 있었고, 그 뒤에 넣은 줄이다. 그때 T_ORD에는 CUS_ID 인덱스가 없었다.

테이블과 인덱스 ​

  • T_ORD 주문: 100,000행 · 896블록 · PK_T_ORD (ORD_SEQ) · IX_ORD_CUS (CUS_ID) (2022년 추가) · 고객 CUS_000123의 주문 50건 (블록 50개에 흩어져 있다)
  • T_ORD_DET 주문상세: 200,000행 · 전체 스캔 807블록 · 주문당 1~3건 · PK_T_ORD_DET (ORD_SEQ, ORD_DET_NO)
  • 통계 최신 · 아래 실행계획은 :cus = 'CUS_000123'

원본 SQL ​

sql
SELECT o.ORD_SEQ, o.ORD_DT, o.ORD_ST, d.det_cnt, d.det_amt
  FROM T_ORD o,
       (SELECT ORD_SEQ, COUNT(*) AS det_cnt, SUM(ORD_QTY * UNT_PRC) AS det_amt
          FROM T_ORD_DET
         WHERE ROWNUM > 0             -- 뷰를 먼저 만들게 (2019.03 강도현)
         GROUP BY ORD_SEQ) d
 WHERE o.CUS_ID = :cus
   AND d.ORD_SEQ = o.ORD_SEQ
 ORDER BY o.ORD_DT DESC

실행계획 학습용 예시 · 교재 08화 규모 ​

xplan
----------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                              | Name       | Starts | E-Rows | A-Rows |    A-Time   | Buffers |  Used-Mem |
----------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                       |            |      1 |        |     50 | 00:00:00.09 |     860 |           |
|   1 |  SORT ORDER BY                         |            |      1 |     50 |     50 | 00:00:00.09 |     860 | 6144  (0) |
|*  2 |   HASH JOIN                            |            |      1 |     50 |     50 | 00:00:00.09 |     860 | 1270K (0) |
|   3 |    TABLE ACCESS BY INDEX ROWID BATCHED | T_ORD      |      1 |     50 |     50 | 00:00:00.01 |      53 |           |
|*  4 |     INDEX RANGE SCAN                   | IX_ORD_CUS |      1 |     50 |     50 | 00:00:00.01 |       2 |           |
|   5 |    VIEW                                |            |      1 |   100K |   100K | 00:00:00.08 |     807 |           |
|   6 |     HASH GROUP BY                      |            |      1 |   100K |   100K | 00:00:00.07 |     807 | 5383K (0) |
|   7 |      COUNT                             |            |      1 |        |   200K | 00:00:00.03 |     807 |           |
|*  8 |       FILTER                           |            |      1 |        |   200K | 00:00:00.02 |     807 |           |
|   9 |        TABLE ACCESS FULL               | T_ORD_DET  |      1 |   200K |   200K | 00:00:00.01 |     807 |           |
----------------------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("D"."ORD_SEQ"="O"."ORD_SEQ")
   4 - access("O"."CUS_ID"=:CUS)
   8 - filter(ROWNUM>0)

문제 ​

  1. 860블록 가운데 어느 줄이 쓸데없는 일인지 찾으시오. 고객 조건이 계획의 어디까지 들어가고 어디서 멈췄는지 그 까닭과 함께 설명하시오.
  2. 이 고객의 주문 50건에 딸린 상세만 묶도록 SQL을 고치시오(방법을 둘 이상 제시하면 좋다). 고친 뒤에도 결과가 같다는 것을 보이시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자