Skip to content
튜닝 연습옵티마이저와 통계
OPT-024

고칠 수 없는 본사 판매 분석 패키지, SQL 셋의 추정이 모두 실제와 다르다

실전DDL유통614 / 700

상황 ​

10화에서 본사에 보낸 「통계 수집 정책서」 후보에 유통지원팀이 회신했다. "분석 패키지는 업체 소스라 SQL을 고칠 수 없습니다. 통계 처방은 원본에 걸어도 좋습니다. 단, 무엇이 바뀌는지 목록과 되돌리는 방법을 함께 주세요." 패키지의 리포트들은 아래 세 SQL의 결과 위에서 조인 방법과 순서를 정한다. 선배는 셋의 E-Rows를 손으로 맞혀 보고 처방을 한 묶음으로 고르라고 했다.

테이블과 인덱스 ​

  • CF10 판매: 500,000행 · 4,583블록 · SLE_DT VARCHAR2(8) · SHP_NO 매장 5곳(1001 ~ 1005, 곳마다 약 10만 건) · REG_EMP_NO 등록 직원(schemas 표에는 없는 열이다. 매장마다 직원 한 명이 등록하고, 직원 번호가 매장 번호와 같다) · 2023년 6월 판매 20,953건
  • CF11 판매상품: 1,096,708행 · 8,204블록 · ITM_NO 88종(1001이 275,135줄로 가장 많고, 가장 적은 상품은 3,032줄) · SLE_UPRC 단가 11종 · 상품마다 단가가 하나다(1001의 단가는 2,500원, 2,500원 판매는 모두 533,248줄)
  • 인덱스: CF10_PK (SLE_NO) · CF11_PK (SLE_NO, ITM_NO) · IX_CF10_SLE_DT (SLE_DT)
  • 통계: CF10 선호도 FOR ALL COLUMNS SIZE 1 · CF11 선호도 FOR ALL COLUMNS SIZE 1 FOR COLUMNS SIZE 254 SLE_UPRC(SLE_UPRC만 FREQUENCY 히스토그램) · 확장 통계 없음

원본 SQL ​

sql
-- (가) 매장 · 등록 직원별 판매 (점장 화면)
SELECT COUNT(*), MIN(SLE_DT), MAX(SLE_DT)
  FROM CF10
 WHERE SHP_NO = 1003
   AND REG_EMP_NO = 1003;

-- (나) 상품 · 단가별 판매 (MD 화면)
SELECT SUM(SLE_QTY), SUM(SLE_QTY * SLE_UPRC)
  FROM CF11
 WHERE ITM_NO = 1001
   AND SLE_UPRC = 2500;

-- (다) 월 판매 (BI 차트 · 판매일자를 날짜로 바꿔 묻는다)
SELECT SHP_NO, COUNT(*)
  FROM CF10
 WHERE TO_DATE(SLE_DT, 'YYYYMMDD') >= DATE '2023-06-01'
   AND TO_DATE(SLE_DT, 'YYYYMMDD') <  DATE '2023-07-01'
 GROUP BY SHP_NO;

실행계획 학습용 예시 · (가)와 (다)의 추정은 교재 10화 복사본 측정 ​

xplan
[(가)]
------------------------------------------------------------------------------------
| Id  | Operation          | Name | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |      1 |        |      1 |00:00:00.05 |    4583 |
|   1 |  SORT AGGREGATE    |      |      1 |      1 |      1 |00:00:00.05 |    4583 |
|*  2 |   TABLE ACCESS FULL| CF10 |      1 |  20000 |   100K |00:00:00.04 |    4583 |
------------------------------------------------------------------------------------
   2 - filter(("SHP_NO"=1003 AND "REG_EMP_NO"=1003))

[(나)]
------------------------------------------------------------------------------------
| Id  | Operation          | Name | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |      1 |        |      1 |00:00:00.08 |    8204 |
|   1 |  SORT AGGREGATE    |      |      1 |      1 |      1 |00:00:00.08 |    8204 |
|*  2 |   TABLE ACCESS FULL| CF11 |      1 |   6060 |   275K |00:00:00.07 |    8204 |
------------------------------------------------------------------------------------
   2 - filter(("ITM_NO"=1001 AND "SLE_UPRC"=2500))

[(다)]
------------------------------------------------------------------------------------
| Id  | Operation          | Name | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |      1 |        |      5 |00:00:00.09 |    4583 |
|   1 |  HASH GROUP BY     |      |      1 |      5 |      5 |00:00:00.09 |    4583 |
|*  2 |   TABLE ACCESS FULL| CF10 |      1 |   1250 |  20953 |00:00:00.08 |    4583 |
------------------------------------------------------------------------------------
   2 - filter((TO_DATE("SLE_DT",'YYYYMMDD')>=TO_DATE(' 2023-06-01 00:00:00', 'syyyy-mm-dd hh24:mi:ss') AND
              TO_DATE("SLE_DT",'YYYYMMDD')<TO_DATE(' 2023-07-01 00:00:00', 'syyyy-mm-dd hh24:mi:ss')))

정확한 A-Rows : (가) 100,167 · (나) 275,135 · (다) 20,953

문제 ​

  1. 세 SQL의 E-Rows(20,000 · 6,060 · 1,250)를 손으로 계산하고, 옵티마이저가 각각 무엇을 잘못 가정했는지 쓰시오.
  2. SQL을 고치지 않는다는 조건에서 세 SQL을 바로잡는 통계 처방을 DDL로 쓰시오. (나)에서 처방을 만들어도 쓰이지 않는 함정도 쓰시오.
  3. 처방이 원본 테이블과 다른 SQL에 주는 영향과, 반영 전에 확인할 것과 되돌리는 방법을 쓰시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자