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

거래처 납품 상품의 판매 실적표에서 상품마다 매출이 서로 다른 배수로 부풀었다

실전SELECT유통266 / 700

상황 ​

구매팀의 「거래처 납품 상품 판매 실적」은 거래처를 고르면 그 거래처에 발주한 적이 있는 상품들의 한 달 판매 수량과 매출을 보여 준다. 처음에 IN 서브쿼리로 짰던 SQL을 이번 달 개편 때 조민우가 조인으로 바꿨다. "발주 쪽 컬럼(최근 발주일)을 나중에 화면에 붙이려고" 바꿨다고 한다. 개편 뒤 첫 보고에서 재무팀이 매출이 판매 원장과 맞지 않는다며 반려했다. 상품마다 어긋난 정도가 37배에서 61배까지 제각각이라, 처음에는 단가 오류를 의심했다.

테이블과 인덱스 ​

  • CF11 판매상품: 1,096,708행 · 8,204블록 · CF11_PK (SLE_NO, ITM_NO) · SLE_NO가 'YYYYMMDD-NNNN' 모양이라 한 달 범위는 CF11_PK로 읽는다(2023년 6월 46,384줄 · 리프 181블록 · 테이블 347블록)
  • CF12 발주: 734행 · 10블록 · CF12_PK (ORD_NO) · 거래처 :ptnr의 발주 61건
  • CF13 발주상품: 7,102행 · 57블록 · CF13_PK (ORD_NO, ITM_NO) · 거래처 :ptnr의 발주 61건에 상품 12종이 590줄(상품마다 37 ~ 61줄)
  • 2023년 6월 그 12종의 판매 6,325줄 · 통계 최신

원본 SQL ​

sql
SELECT b.ITM_NO,
       SUM(b.SLE_QTY)              AS qty,
       SUM(b.SLE_QTY * b.SLE_UPRC) AS amt
  FROM CF11 b, CF13 d, CF12 h
 WHERE b.SLE_NO >= :v_from          -- '20230601'
   AND b.SLE_NO <  :v_to            -- '20230701'
   AND d.ITM_NO  = b.ITM_NO
   AND h.ORD_NO  = d.ORD_NO
   AND h.PTNR_NO = :ptnr
 GROUP BY b.ITM_NO
 ORDER BY amt DESC

실행계획 학습용 예시 ​

xplan
---------------------------------------------------------------------------------------------------------
| Id  | Operation                              | Name    | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
---------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                       |         |      1 |        |     12 |00:00:00.24 |     595 |
|   1 |  SORT ORDER BY                         |         |      1 |     12 |     12 |00:00:00.24 |     595 |
|   2 |   HASH GROUP BY                        |         |      1 |     12 |     12 |00:00:00.24 |     595 |
|*  3 |    HASH JOIN                           |         |      1 |    311K|    311K|00:00:00.13 |     595 |
|*  4 |     HASH JOIN                          |         |      1 |    590 |    590 |00:00:00.01 |      67 |
|*  5 |      TABLE ACCESS FULL                 | CF12    |      1 |     61 |     61 |00:00:00.01 |      10 |
|   6 |      TABLE ACCESS FULL                 | CF13    |      1 |   7102 |   7102 |00:00:00.01 |      57 |
|   7 |     TABLE ACCESS BY INDEX ROWID BATCHED| CF11    |      1 |  46384 |  46384 |00:00:00.02 |     528 |
|*  8 |      INDEX RANGE SCAN                  | CF11_PK |      1 |  46384 |  46384 |00:00:00.01 |     181 |
---------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   3 - access("D"."ITM_NO"="B"."ITM_NO")
   4 - access("H"."ORD_NO"="D"."ORD_NO")
   5 - filter("H"."PTNR_NO"=:PTNR)
   8 - access("B"."SLE_NO">=:V_FROM AND "B"."SLE_NO"<:V_TO)

문제 ​

  1. 매출이 상품마다 다른 배수로 부푼 이유를 실행계획의 줄 번호와 A-Rows로 설명하시오. 처음에 IN으로 짠 SQL에서는 왜 이런 일이 없었는지도 설명하시오.
  2. (가) 조인한 뒤 판매 줄에 DISTINCT를 거는 안, (나) 조인한 뒤 SUM(DISTINCT …)로 더하는 안, (다) 발주 쪽을 상품당 한 줄로 먼저 만든 뒤 조인하는 안, (라) 원래의 IN 서브쿼리로 돌아가는 안의 결과와 일량을 비교하시오.
  3. 최근 발주일을 화면에 함께 보일 수 있도록 SQL을 고치고, 고친 SQL이 맞다는 것을 무엇과 어떻게 대조해 보일지 쓰시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자