테마
CRM팀의 VIP 관리 화면에는 SQL 세 개가 있다. Q1 「VIP 목록」(A 등급 20명의 올해 주문 건수와 금액)은 하루 300번, Q2 「고객 최근 주문 10건」은 콜센터가 하루 4,000번, Q3 「상품별 평균 평점」은 하루 50번 돈다. 고객사는 "스칼라 서브쿼리가 있는 SQL마다 인덱스를 하나씩 만들어 달라"라고 요청했다. 박팀장은 인덱스를 최소한으로 정하고, 필요 없는 곳은 왜 필요 없는지 근거를 대라고 했다.
T_ORD_BIG: 1,000,000행 · 5,826블록 · 인덱스 없음 · 고객마다 500건(2,000행 간격으로 흩어져 있다) · 2024년 333,245건(VIP 20명은 3,330건, 1명당 약 167건)M_CUS: 2,000행 · 30블록 · PK_M_CUS (CUS_ID) · CUS_GD A 20명M_ITM: 100행 · 5블록 · PK_M_ITM · T_ITM_EVL: 5,000행 · 48블록 · PK_T_ITM_EVL (ITM_ID, EVL_LST_NO) · 상품당 50건-- Q1 「VIP 목록」 (하루 300 번)
SELECT c.CUS_ID, c.CUS_NM,
(SELECT COUNT(*) FROM T_ORD_BIG o
WHERE o.CUS_ID = c.CUS_ID
AND o.ORD_DT >= DATE '2024-01-01' AND o.ORD_DT < DATE '2025-01-01') AS ord_cnt,
(SELECT SUM(o.ORD_AMT) FROM T_ORD_BIG o
WHERE o.CUS_ID = c.CUS_ID
AND o.ORD_DT >= DATE '2024-01-01' AND o.ORD_DT < DATE '2025-01-01') AS ord_amt
FROM M_CUS c
WHERE c.CUS_GD = 'A'
ORDER BY c.CUS_ID;
-- Q2 「고객 최근 주문 10건」 (하루 4,000 번)
SELECT *
FROM (SELECT o.ORD_SEQ, o.ORD_DT, o.ORD_ST, o.ORD_AMT
FROM T_ORD_BIG o
WHERE o.CUS_ID = :cus
ORDER BY o.ORD_DT DESC)
WHERE ROWNUM <= 10;
-- Q3 「상품별 평균 평점」 (하루 50 번)
SELECT i.ITM_ID, i.ITM_NM,
(SELECT ROUND(AVG(e.EVL_PT), 2) FROM T_ITM_EVL e WHERE e.ITM_ID = i.ITM_ID) AS avg_pt
FROM M_ITM i
ORDER BY i.ITM_ID;
[Q1]
-----------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
-----------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 20 |00:00:00.29 | 11682 |
| 1 | SORT ORDER BY | | 1 | 20 | 20 |00:00:00.29 | 11682 |
|* 2 | HASH JOIN OUTER | | 1 | 20 | 20 |00:00:00.29 | 11682 |
|* 3 | HASH JOIN OUTER | | 1 | 20 | 20 |00:00:00.15 | 5856 |
|* 4 | TABLE ACCESS FULL | M_CUS | 1 | 20 | 20 |00:00:00.01 | 30 |
| 5 | VIEW | VW_SSQ_1 | 1 | 2000 | 2000 |00:00:00.14 | 5826 |
| 6 | HASH GROUP BY | | 1 | 2000 | 2000 |00:00:00.14 | 5826 |
|* 7 | TABLE ACCESS FULL| T_ORD_BIG | 1 | 333K| 333K|00:00:00.05 | 5826 |
| 8 | VIEW | VW_SSQ_2 | 1 | 2000 | 2000 |00:00:00.14 | 5826 |
| 9 | HASH GROUP BY | | 1 | 2000 | 2000 |00:00:00.14 | 5826 |
|* 10 | TABLE ACCESS FULL | T_ORD_BIG | 1 | 333K| 333K|00:00:00.05 | 5826 |
-----------------------------------------------------------------------------------------------
2 - access("VW_SSQ_2"."ITEM_1"(+)="C"."CUS_ID")
3 - access("VW_SSQ_1"."ITEM_1"(+)="C"."CUS_ID")
4 - filter("C"."CUS_GD"='A')
7 - filter(("O"."ORD_DT">=TO_DATE(' 2024-01-01 00:00:00', 'syyyy-mm-dd hh24:mi:ss') AND "O"."ORD_DT"<TO_DATE(' 2025-01-01 00:00:00', 'syyyy-mm-dd hh24:mi:ss')))
10 - filter((같은 모양))
[Q2]
-----------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
-----------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 10 |00:00:00.05 | 5826 |
|* 1 | COUNT STOPKEY | | 1 | | 10 |00:00:00.05 | 5826 |
| 2 | VIEW | | 1 | 500 | 10 |00:00:00.05 | 5826 |
|* 3 | SORT ORDER BY STOPKEY| | 1 | 500 | 10 |00:00:00.05 | 5826 |
|* 4 | TABLE ACCESS FULL | T_ORD_BIG | 1 | 500 | 500 |00:00:00.05 | 5826 |
-----------------------------------------------------------------------------------------------
1 - filter(ROWNUM<=10)
3 - filter(ROWNUM<=10)
4 - filter("O"."CUS_ID"=:CUS)
[Q3]
-----------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
-----------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 100 |00:00:00.01 | 53 |
| 1 | SORT ORDER BY | | 1 | 100 | 100 |00:00:00.01 | 53 |
|* 2 | HASH JOIN OUTER | | 1 | 100 | 100 |00:00:00.01 | 53 |
| 3 | TABLE ACCESS FULL | M_ITM | 1 | 100 | 100 |00:00:00.01 | 5 |
| 4 | VIEW | VW_SSQ_1 | 1 | 100 | 100 |00:00:00.01 | 48 |
| 5 | HASH GROUP BY | | 1 | 100 | 100 |00:00:00.01 | 48 |
| 6 | TABLE ACCESS FULL | T_ITM_EVL | 1 | 5000 | 5000 |00:00:00.01 | 48 |
-----------------------------------------------------------------------------------------------
2 - access("VW_SSQ_1"."ITEM_1"(+)="I"."ITM_ID")
ORD_AMT를 인덱스에 넣을지를 비교하고, 인덱스를 더하는 대가를 서술하시오.