테마
이 문제는 교재 18화 졸업 과제와 같은 이커머스 데이터베이스를 씁니다. 『튜너의 1년』을 순서대로 읽고 있다면 졸업 과제를 먼저 풀고 오세요. 아래 「테이블과 인덱스」 칸에 과제의 답 일부(제약 조건, 인덱스, 통계의 실제 상태)가 나옵니다.
이커머스 추천팀은 매일 새벽 상품 조회 이력을 추천 엔진이 읽는 적재 테이블로 옮긴다. 신입 개발자가 교육에서 배운 대로 BULK COLLECT와 FORALL로 짰다며 리뷰를 부탁했다.
조민우가 한 번 돌리고 세션 통계를 떠 보니 recursive calls가 300,232였다. "천 건씩 묶었으면 몇백 번이어야 하지 않나요?"
product_views: 100,000행 · 367블록 · PK(view_id) · product_id · customer_id · view_date DATE(시분초 있음)rec_view_feed(적재 대상, 이 문제용): view_id · product_id · customer_id · view_ts VARCHAR2(19) · day_tp VARCHAR2(10) · score NUMBER · load_dt DATE · 인덱스 없음 · 배치마다 TRUNCATEPKG_REC_SCORE.DECAY(product_id, view_date): SQL 없이 패키지 배열로 최근성 가중치를 계산하는 PL/SQL 함수. 추천 화면도 같은 함수를 쓴다.PROCEDURE LOAD_VIEW_FEED IS
CURSOR c IS SELECT view_id, product_id, customer_id, view_date FROM product_views;
TYPE t_num IS TABLE OF NUMBER INDEX BY PLS_INTEGER;
TYPE t_dt IS TABLE OF DATE INDEX BY PLS_INTEGER;
TYPE t_vc IS TABLE OF VARCHAR2(20) INDEX BY PLS_INTEGER;
v_id t_num; v_pid t_num; v_cid t_num; v_dt t_dt;
v_ts t_vc; v_dy t_vc; v_sc t_num; v_ld t_dt;
BEGIN
EXECUTE IMMEDIATE 'TRUNCATE TABLE rec_view_feed';
OPEN c;
LOOP
FETCH c BULK COLLECT INTO v_id, v_pid, v_cid, v_dt LIMIT 1000;
EXIT WHEN v_id.COUNT = 0;
FOR i IN 1 .. v_id.COUNT LOOP
SELECT TO_CHAR(v_dt(i), 'YYYY-MM-DD"T"HH24:MI:SS') INTO v_ts(i) FROM dual;
SELECT DECODE(TO_CHAR(v_dt(i), 'DY', 'NLS_DATE_LANGUAGE=ENGLISH'),
'SAT', 'WEEKEND', 'SUN', 'WEEKEND', 'WEEKDAY') INTO v_dy(i) FROM dual;
SELECT SYSDATE INTO v_ld(i) FROM dual;
v_sc(i) := PKG_REC_SCORE.DECAY(v_pid(i), v_dt(i));
END LOOP;
FORALL i IN 1 .. v_id.COUNT
INSERT INTO rec_view_feed (view_id, product_id, customer_id, view_ts, day_tp, score, load_dt)
VALUES (v_id(i), v_pid(i), v_cid(i), v_ts(i), v_dy(i), v_sc(i), v_ld(i));
END LOOP;
CLOSE c;
COMMIT;
END;
[세션 통계 — LOAD_VIEW_FEED 한 번 (V$MYSTAT 증가분)]
경과 시간 1.02 ~ 1.08 초
CPU used by this session 0.99 초
recursive calls 300,232
execute count 300,128
session logical reads 2,436
[TKPROF — 문장별 요약 (sys=no)]
SQL Execute Fetch rows query elapsed
SELECT TO_CHAR(:B1 , 'YYYY-MM-DD"T"HH24:MI:SS') FROM DUAL 100,000 100,000 100,000 0 0.27
SELECT DECODE(TO_CHAR(:B1 , 'DY', …), …) FROM DUAL 100,000 100,000 100,000 0 0.28
SELECT SYSDATE FROM DUAL 100,000 100,000 100,000 0 0.25
INSERT INTO REC_VIEW_FEED (…) VALUES (:B7 , …) 100 0 100,000 19 0.09
SELECT VIEW_ID, PRODUCT_ID, CUSTOMER_ID, VIEW_DATE FROM … 1 101 100,000 468 0.02
(DUAL 세 문장의 계획은 FAST DUAL · Buffers 0)