테마
이 문제는 교재 18화 졸업 과제와 같은 이커머스 데이터베이스를 씁니다. 『튜너의 1년』을 순서대로 읽고 있다면 졸업 과제를 먼저 풀고 오세요. 아래 「테이블과 인덱스」 칸에 과제의 답 일부(제약 조건, 인덱스, 통계의 실제 상태)가 나옵니다.
마케팅팀의 「관심 상품 푸시」 배치는 매일 05시에 지난 7일 동안 상품을 보고도 주문하지 않은 고객을 PUSH_TARGET에 넣는다. 원래 customer_id NOT IN (SELECT … FROM orders …)으로 짜여 있었는데, 지난주 코드 리뷰에서 "NOT IN은 NULL 때문에 위험하니 NOT EXISTS로 바꾸라"라는 지적을 받고 바꿨다.
바꾼 다음 날부터 배치가 ORA-01400: cannot insert NULL into ("ECOMMERCE"."PUSH_TARGET"."CUSTOMER_ID")로 멈춘다. 그 전 3주 동안은 날마다 200명 안팎이 들어갔다.
product_views 상품 조회 로그: 100,000행 · 367블록 · PK만 있음 · customer_id는 NULL 허용(비로그인 조회는 NULL)orders: 34,908행 · 193블록 · PK만 있음 · customer_id는 NOT NULL · 같은 7일 주문 134건 · 조회한 고객 268명 가운데 67명이 그 기간에 주문했다(그 고객들의 조회는 87건)PUSH_TARGET 푸시 대상(마케팅팀 표): PUSH_DT DATE · CUSTOMER_ID NUMBER NOT NULL · PK (PUSH_DT, CUSTOMER_ID)INSERT INTO PUSH_TARGET (PUSH_DT, CUSTOMER_ID)
SELECT DISTINCT TRUNC(SYSDATE), v.customer_id
FROM product_views v
WHERE v.view_date >= TRUNC(SYSDATE) - 7
AND v.view_date < TRUNC(SYSDATE)
AND NOT EXISTS (SELECT 1
FROM orders o
WHERE o.customer_id = v.customer_id
AND o.order_date >= TRUNC(SYSDATE) - 7
AND o.order_date < TRUNC(SYSDATE))
------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | Used-Mem |
------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 202 |00:00:00.01 | 560 | |
| 1 | HASH UNIQUE | | 1 | 375 | 202 |00:00:00.01 | 560 | 1287K (0)|
|* 2 | HASH JOIN RIGHT ANTI| | 1 | 375 | 297 |00:00:00.01 | 560 | 1453K (0)|
|* 3 | TABLE ACCESS FULL | ORDERS | 1 | 134 | 134 |00:00:00.01 | 193 | |
|* 4 | TABLE ACCESS FULL | PRODUCT_VIEWS | 1 | 384 | 384 |00:00:00.01 | 367 | |
------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("O"."CUSTOMER_ID"="V"."CUSTOMER_ID")
3 - filter(("O"."ORDER_DATE">=TRUNC(SYSDATE@!)-7 AND "O"."ORDER_DATE"<TRUNC(SYSDATE@!)))
4 - filter(("V"."VIEW_DATE">=TRUNC(SYSDATE@!)-7 AND "V"."VIEW_DATE"<TRUNC(SYSDATE@!)))
[결과] 202행 · 그 가운데 CUSTOMER_ID 가 NULL 인 행 1개
[바꾸기 전 NOT IN] 2번 줄이 HASH JOIN RIGHT ANTI SNA · A-Rows 226 · 결과 201행 · Buffers 560