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

권역을 하나 더하면 월 주문 집계 배치도 그만큼 길어진다

중급INSERT버거294 / 700

상황 ​

본사 영업기획팀의 「권역별 월 주문 수」는 서울, 경기, 부산 세 권역의 2013년 월별 주문 수를 요약 테이블 RGN_MON_ORD에 담는다. 매장 정보가 정정될 때마다 1년 치를 지우고 다시 넣는데, 한 번에 3.4초가 걸리고 그동안 디스크 읽기가 몰린다. 다음 달부터 대구 권역을 더해 달라는 요청이 왔다. 개발자는 "UNION ALL에 갈래 하나만 더 붙이면 된다"라고 하고, DBA는 "그러면 4.5초가 된다"라고 한다.

테이블과 인덱스 ​

  • ORD 주문: 1,000,000행 · 22,422블록 · ORD_DT VARCHAR2(8) · 2013년 500,218건 · SHOP_NO · ORD_X01 (ORD_DT, ORD_HMS)가 있지만 1년 치를 읽을 때는 FULL이 낫다
  • SHOP 매장: 200행 · 5블록 · ADDR 주소(앞 두 글자가 시도: 서울 62곳 · 경기 41곳 · 부산 10곳 · 그 밖 87곳)
  • RGN_MON_ORD 권역 월 주문 (이 문제에서 쓰는 파생 테이블): RGN_CD NOT NULL · ORD_YM · ORD_CNT · 36행(3권역 × 12개월)
  • 통계 최신 · 배치 시간대에는 ORD가 버퍼 캐시에 남아 있지 않다

원본 SQL ​

sql
INSERT INTO RGN_MON_ORD (RGN_CD, ORD_YM, ORD_CNT)
SELECT 'SEOUL', SUBSTR(o.ORD_DT, 1, 6), COUNT(*)
  FROM ORD o, SHOP s
 WHERE s.SHOP_NO = o.SHOP_NO
   AND s.ADDR LIKE '서울%'
   AND o.ORD_DT BETWEEN '20130101' AND '20131231'
 GROUP BY SUBSTR(o.ORD_DT, 1, 6)
UNION ALL
SELECT 'GYEONGGI', SUBSTR(o.ORD_DT, 1, 6), COUNT(*)
  FROM ORD o, SHOP s
 WHERE s.SHOP_NO = o.SHOP_NO
   AND s.ADDR LIKE '경기%'
   AND o.ORD_DT BETWEEN '20130101' AND '20131231'
 GROUP BY SUBSTR(o.ORD_DT, 1, 6)
UNION ALL
SELECT 'BUSAN', SUBSTR(o.ORD_DT, 1, 6), COUNT(*)
  FROM ORD o, SHOP s
 WHERE s.SHOP_NO = o.SHOP_NO
   AND s.ADDR LIKE '부산%'
   AND o.ORD_DT BETWEEN '20130101' AND '20131231'
 GROUP BY SUBSTR(o.ORD_DT, 1, 6)

실행계획 학습용 예시 ​

xplan
------------------------------------------------------------------------------------------------------------
| Id  | Operation                 | Name        | Starts | E-Rows | A-Rows |      A-Time | Buffers | Reads |
------------------------------------------------------------------------------------------------------------
|   0 | INSERT STATEMENT          |             |      1 |        |      0 | 00:00:03.41 |   67293 | 67266 |
|   1 |  LOAD TABLE CONVENTIONAL  | RGN_MON_ORD |      1 |        |      0 | 00:00:03.41 |   67293 | 67266 |
|   2 |   UNION-ALL               |             |      1 |        |     36 | 00:00:03.40 |   67281 | 67266 |
|   3 |    HASH GROUP BY          |             |      1 |     12 |     12 | 00:00:01.14 |   22427 | 22422 |
|*  4 |     HASH JOIN             |             |      1 |   150K |   155K | 00:00:01.10 |   22427 | 22422 |
|*  5 |      TABLE ACCESS FULL    | SHOP        |      1 |     60 |     62 | 00:00:00.01 |       5 |     0 |
|*  6 |      TABLE ACCESS FULL    | ORD         |      1 |   500K |   500K | 00:00:00.97 |   22422 | 22422 |
|   7 |    HASH GROUP BY          |             |      1 |     12 |     12 | 00:00:01.13 |   22427 | 22422 |
|*  8 |     HASH JOIN             |             |      1 |   100K |   102K | 00:00:01.09 |   22427 | 22422 |
|*  9 |      TABLE ACCESS FULL    | SHOP        |      1 |     40 |     41 | 00:00:00.01 |       5 |     0 |
|* 10 |      TABLE ACCESS FULL    | ORD         |      1 |   500K |   500K | 00:00:00.96 |   22422 | 22422 |
|  11 |    HASH GROUP BY          |             |      1 |     12 |     12 | 00:00:01.13 |   22427 | 22422 |
|* 12 |     HASH JOIN             |             |      1 |  25009 |  25011 | 00:00:01.09 |   22427 | 22422 |
|* 13 |      TABLE ACCESS FULL    | SHOP        |      1 |     10 |     10 | 00:00:00.01 |       5 |     0 |
|* 14 |      TABLE ACCESS FULL    | ORD         |      1 |   500K |   500K | 00:00:00.96 |   22422 | 22422 |
------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   4 - access("S"."SHOP_NO"="O"."SHOP_NO")
   5 - filter("S"."ADDR" LIKE '서울%')
   6 - filter(("O"."ORD_DT">='20130101' AND "O"."ORD_DT"<='20131231'))
   8 - access("S"."SHOP_NO"="O"."SHOP_NO")
   9 - filter("S"."ADDR" LIKE '경기%')
  10 - filter(("O"."ORD_DT">='20130101' AND "O"."ORD_DT"<='20131231'))
  12 - access("S"."SHOP_NO"="O"."SHOP_NO")
  13 - filter("S"."ADDR" LIKE '부산%')
  14 - filter(("O"."ORD_DT">='20130101' AND "O"."ORD_DT"<='20131231'))

문제 ​

  1. 3.4초가 어디서 나오는지 실행계획의 줄 번호와 Buffers, Reads로 설명하고, 권역을 하나 더하면 얼마가 되는지 쓰시오.
  2. 권역이 늘어도 ORD를 한 번만 읽도록 INSERT를 고치시오. 결과 36행이 바뀌지 않으려면 무엇을 지켜야 하는지도 쓰시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자