Skip to content
튜닝 연습소트 튜닝과 페이징
SORT-023

「주문 관리」 뒤 쪽이 느린데, 고객명 칸을 빼 보니 훨씬 빨라졌다

기초SELECT한빛유통328 / 700

상황 ​

CS팀의 「주문 관리」 화면은 전체 주문을 최신순(주문번호 역순)으로 한 쪽에 20건씩 보여 주고, 주문마다 고객명과 등급을 함께 띄운다. 상담원은 쪽 번호로 이동한다. 뒤 쪽으로 갈수록 느려진다는 말에 개발자가 시험 삼아 고객명과 등급 칸을 빼 보니 50쪽이 수십 배 빨라졌다. 아래는 칸을 빼기 전, 50쪽(981 ~ 1,000번째)을 열었을 때의 실행계획이다.

테이블과 인덱스 ​

  • T_ORD 주문: 100,000행 · 896블록 · PK_T_ORD (ORD_SEQ) · 주문번호 순으로 쌓여 PK 순서와 테이블 순서가 같다.
  • M_CUS 고객: 2,000행 · 30블록 · PK_M_CUS (CUS_ID)
  • T_ORD.CUS_ID는 NOT NULL이고, M_CUS로 가는 FK가 켜져 있다. 통계는 최신이다.

원본 SQL ​

sql
SELECT *
  FROM (SELECT ROWNUM rn, a.*
          FROM (SELECT o.ORD_SEQ, o.ORD_DT, o.ORD_ST, o.ORD_AMT, c.CUS_NM, c.CUS_GD
                  FROM T_ORD o, M_CUS c
                 WHERE c.CUS_ID = o.CUS_ID
                 ORDER BY o.ORD_SEQ DESC) a
         WHERE ROWNUM <= :page * 20)
 WHERE rn > (:page - 1) * 20

실행계획 학습용 예시 ​

xplan
[50쪽 · :page = 50]
--------------------------------------------------------------------------------------------------------
| Id  | Operation                          | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
--------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                   |          |      1 |        |     20 |00:00:00.01 |    1033 |
|*  1 |  VIEW                              |          |      1 |   1000 |     20 |00:00:00.01 |    1033 |
|*  2 |   COUNT STOPKEY                    |          |      1 |        |   1000 |00:00:00.01 |    1033 |
|   3 |    VIEW                            |          |      1 |    100K|   1000 |00:00:00.01 |    1033 |
|   4 |     NESTED LOOPS                   |          |      1 |    100K|   1000 |00:00:00.01 |    1033 |
|   5 |      NESTED LOOPS                  |          |      1 |    100K|   1000 |00:00:00.01 |      33 |
|   6 |       TABLE ACCESS BY INDEX ROWID  | T_ORD    |      1 |    100K|   1000 |00:00:00.01 |      12 |
|   7 |        INDEX FULL SCAN DESCENDING  | PK_T_ORD |      1 |   1000 |   1000 |00:00:00.01 |       3 |
|*  8 |       INDEX UNIQUE SCAN            | PK_M_CUS |   1000 |      1 |   1000 |00:00:00.01 |      21 |
|   9 |      TABLE ACCESS BY INDEX ROWID   | M_CUS    |   1000 |      1 |   1000 |00:00:00.01 |    1000 |
--------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter("RN">(:PAGE-1)*20)
   2 - filter(ROWNUM<=:PAGE*20)
   8 - access("C"."CUS_ID"="O"."CUS_ID")

문제 ​

  1. 화면에 보이는 것은 20건인데 M_CUS를 몇 번 찾아가는지 실행계획의 Starts로 쓰고, 그중 몇 번이 필요 없는 일인지 설명하시오.
  2. 결과가 바뀌지 않게 SQL을 고치고, 그렇게 고쳐도 결과가 같다고 말할 수 있는 근거를 쓰시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자