Skip to content
튜닝 연습인덱스 사용 조건
IDX-015

매장 하나는 주문 7건인데 200곳 일 마감이 12만 블록을 읽는다

중급PLSQL버거15 / 700

상황 ​

버거원 본사는 매일 23시 30분에 일 마감 프로시저를 돌려 매장마다 그날 주문 수를 SHOP_DAY_CLOSE에 쌓는다. 매장 코드는 옛 POS에서 숫자(1 ~ 200)였고, 프로시저는 그 시절 루프를 그대로 쓴다. 가맹점이 늘면서 마감 시각이 밀린다며 오과장이 TKPROF를 떠 왔다. 아래는 2013년 3월 15일 마감분이다.

테이블과 인덱스 ​

  • ORD: 1,000,000행 · 22,422블록 · 주문 일시 순으로 쌓였다 · SHOP_NO VARCHAR2(10) 'SH0001' ~ 'SH0200'(200곳에 고르게) · ORD_DT VARCHAR2(8)
  • 2013년 3월 15일 주문 1,344건(매장당 평균 약 7건)
  • SHOP: 200행 · 5블록 · PK SHOP_PK (SHOP_NO)
  • 인덱스 ORD_X02 (SHOP_NO, ORD_DT): 가맹점주 앱용 · BLEVEL 2 · 리프 약 3,100블록 · ORD_X01 (ORD_DT, ORD_HMS) · 통계 최신
  • SHOP_DAY_CLOSE (SHOP_NO, CLOSE_DT, ORD_CNT): 하루 200행씩 쌓는 마감 테이블 · 인덱스 없음

원본 SQL ​

sql
PROCEDURE P_DAY_CLOSE(p_dt VARCHAR2) IS
  v_cnt NUMBER;
BEGIN
  FOR i IN 1 .. 200 LOOP
    SELECT COUNT(*) INTO v_cnt
      FROM ORD
     WHERE TO_NUMBER(SUBSTR(SHOP_NO, 3)) = i
       AND ORD_DT = p_dt;

    INSERT INTO SHOP_DAY_CLOSE (SHOP_NO, CLOSE_DT, ORD_CNT)
    VALUES ('SH' || LPAD(i, 4, '0'), p_dt, v_cnt);
  END LOOP;
  COMMIT;
END;

실행계획 학습용 예시 · TKPROF 요약 ​

xplan
[TKPROF — 루프 안의 SELECT]
SELECT COUNT(*) FROM ORD WHERE TO_NUMBER(SUBSTR(SHOP_NO, 3)) = :B2 AND ORD_DT = :B1

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute    200      0.01       0.01          0          0          0           0
Fetch      200      1.02       1.05          0     122400          0         200
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total      401      1.03       1.06          0     122400          0         200

[DBMS_XPLAN — 마지막 한 번 (i = 200)]
----------------------------------------------------------------------------------------
| Id  | Operation        | Name    | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT |         |      1 |        |      1 |00:00:00.01 |     612 |
|   1 |  SORT AGGREGATE  |         |      1 |      1 |      1 |00:00:00.01 |     612 |
|*  2 |   INDEX SKIP SCAN| ORD_X02 |      1 |      1 |      7 |00:00:00.01 |     612 |
----------------------------------------------------------------------------------------
   2 - access("ORD_DT"=:B1)
       filter(("ORD_DT"=:B1 AND TO_NUMBER(SUBSTR("SHOP_NO",3))=:B2))

문제 ​

  1. 매장 하나의 주문은 7건 남짓인데 SELECT 한 번에 612블록을 읽는 이유를 실행계획의 2번 줄로 설명하시오. 하루 마감 전체의 일량도 계산하시오.
  2. 프로시저를 고쳐 일량을 줄이시오. 루프를 남긴다면 SELECT의 조건을 어떻게 바꿀지, 루프를 없앤다면 어떤 한 문장으로 바꿀지 쓰시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자