Skip to content
튜닝 연습조인 튜닝
JOIN-065

매장을 바꿔 골라도 「매장 일 판매 현황」의 숫자가 똑같다

중급SELECT유통215 / 700

상황 ​

매장 관리 화면의 「매장 일 판매 현황」은 매장과 날짜를 고르면 그날 상품별 판매 수량과 금액을 금액 순으로 보여 준다. 지난주 화면 머리에 매장명을 띄워 달라는 요청이 있어, 개발자가 SQL에 CF07을 붙이고 매장 조건을 CF07 쪽으로 옮겼다. 오늘 아침 강남점 점장이 "우리 매장 숫자가 홍대점이랑 똑같다"라고 알려 왔다. DBA가 실행계획을 열어 보니 MERGE JOIN CARTESIAN이 있었는데, E-Rows와 A-Rows는 모두 맞아떨어졌다. 아래는 홍대점(SHP_NO = 1003)의 2023-06-15를 조회한 실행계획이다.

테이블과 인덱스 ​

  • CF07 매장: 6행 · 4블록 · CF07_PK (SHP_NO) · 1000은 본사(판매 없음), 판매 매장은 1001 ~ 1005의 5곳
  • CF10 판매: 500,000행 · 4,583블록 · CF10_PK (SLE_NO) · IX_CF10_SLE_DT (SLE_DT)(하루치가 약 16블록에 모여 있다) · 2023-06-15 판매 687건, 그중 1003 매장 139건
  • CF11 판매상품: 1,096,708행 · 8,204블록 · CF11_PK (SLE_NO, ITM_NO) BLEVEL 2 · 그날 판매상품 1,512줄(상품 71개), 그중 1003 매장 305줄(상품 58개)
  • CF01 상품: 109행 · 5블록 · CF01_PK (ITM_NO) · 통계 최신

원본 SQL ​

sql
SELECT s.SHP_NM, b.ITM_NO, p.ITM_NM,
       SUM(b.SLE_QTY) AS sle_qty, SUM(b.SLE_QTY * b.SLE_UPRC) AS sle_amt
  FROM CF07 s, CF10 a, CF11 b, CF01 p
 WHERE s.SHP_NO = :v_shp
   AND a.SLE_DT = :v_dt
   AND b.SLE_NO = a.SLE_NO
   AND p.ITM_NO = b.ITM_NO
 GROUP BY s.SHP_NM, b.ITM_NO, p.ITM_NM
 ORDER BY sle_amt DESC

실행계획 학습용 예시 ​

xplan
------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                                   | Name           | Starts | E-Rows | A-Rows |   A-Time   | Buffers |  Used-Mem |
------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                            |                |      1 |        |     71 |00:00:00.03 |    2093 |           |
|   1 |  SORT ORDER BY                              |                |      1 |     71 |     71 |00:00:00.03 |    2093 |  9216 (0) |
|   2 |   HASH GROUP BY                             |                |      1 |     71 |     71 |00:00:00.03 |    2093 | 1123K (0) |
|*  3 |    HASH JOIN                                |                |      1 |   1507 |   1512 |00:00:00.02 |    2093 | 1321K (0) |
|   4 |     TABLE ACCESS FULL                       | CF01           |      1 |    109 |    109 |00:00:00.01 |       5 |           |
|   5 |     NESTED LOOPS                            |                |      1 |   1507 |   1512 |00:00:00.02 |    2088 |           |
|   6 |      MERGE JOIN CARTESIAN                   |                |      1 |    685 |    687 |00:00:00.01 |      22 |           |
|   7 |       TABLE ACCESS BY INDEX ROWID           | CF07           |      1 |      1 |      1 |00:00:00.01 |       2 |           |
|*  8 |        INDEX UNIQUE SCAN                    | CF07_PK        |      1 |      1 |      1 |00:00:00.01 |       1 |           |
|   9 |       BUFFER SORT                           |                |      1 |    685 |    687 |00:00:00.01 |      20 | 79872 (0) |
|  10 |        TABLE ACCESS BY INDEX ROWID BATCHED  | CF10           |      1 |    685 |    687 |00:00:00.01 |      20 |           |
|* 11 |         INDEX RANGE SCAN                    | IX_CF10_SLE_DT |      1 |    685 |    687 |00:00:00.01 |       4 |           |
|  12 |      TABLE ACCESS BY INDEX ROWID            | CF11           |    687 |      2 |   1512 |00:00:00.01 |    2066 |           |
|* 13 |       INDEX RANGE SCAN                      | CF11_PK        |    687 |      2 |   1512 |00:00:00.01 |    1376 |           |
------------------------------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   3 - access("P"."ITM_NO"="B"."ITM_NO")
   8 - access("S"."SHP_NO"=:V_SHP)
  11 - access("A"."SLE_DT"=:V_DT)
  13 - access("B"."SLE_NO"="A"."SLE_NO")

문제 ​

  1. 실행계획에 MERGE JOIN CARTESIAN이 생긴 이유와, 6번 줄과 9번 줄의 E-Rows와 A-Rows가 맞는다는 사실이 무엇을 알려 주는지 설명하시오. 화면에 나온 숫자가 무엇의 합인지도 쓰시오.
  2. SQL을 고치고, 고친 뒤 일량과 결과가 어떻게 바뀌는지 숫자로 쓰시오. 같은 실수를 막을 SQL 작성 규칙도 제안하시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자