Skip to content
튜닝 연습소트 튜닝과 페이징
SORT-053

포인트 내역에서 사용과 적립이 같은 초에 찍힌 두 줄은 잔액이 똑같이 나온다

중급SELECT이커머스358 / 700

이 문제는 교재 18화 졸업 과제와 같은 이커머스 데이터베이스를 씁니다. 『튜너의 1년』을 순서대로 읽고 있다면 졸업 과제를 먼저 풀고 오세요. 아래 「테이블과 인덱스」 칸에 과제의 답 일부(제약 조건, 인덱스, 통계의 실제 상태)가 나옵니다.

상황 ​

고객센터 「포인트 내역」 화면은 고객 한 명의 포인트 거래를 최신순으로 보여 주고, 줄마다 그 거래 직후의 잔액을 붙인다. 상담원 40명이 하루 약 3만 번 연다. 상담원이 캡처를 보내왔다. "주문할 때 포인트를 쓰고 같은 주문으로 적립도 받은 고객인데, 사용 줄과 적립 줄의 잔액이 똑같아요. 사용 직후 잔액이 안 보여서 설명을 못 했어요." 주문을 확정하는 프로시저가 사용과 적립을 같은 초에 넣는다.

테이블과 인덱스 ​

  • point_transactions: 45,000행 · 210블록 · tx_id(시퀀스로 넣는 순서대로 매긴다) · customer_id · amount(늘 양수) · tx_type(EARN · USE · EXPIRE) · created_at DATE(초까지)
  • 고객 5,230명, 고객당 평균 8.6건이고 많은 고객은 200건이 넘는다. 같은 고객의 같은 초 거래가 3,108쌍 있다.
  • 잔액은 EARN은 더하고 USE와 EXPIRE는 뺀 누적이다. 화면의 잔액은 고객의 첫 거래부터 계산한다.
  • 인덱스는 PK(tx_id)뿐이다. 통계는 최신이다.

원본 SQL ​

sql
SELECT tx_id, tx_type, amount, created_at,
       SUM(CASE tx_type WHEN 'EARN' THEN amount ELSE -amount END)
         OVER (ORDER BY created_at) AS balance
  FROM point_transactions
 WHERE customer_id = :cus
 ORDER BY created_at DESC

실행계획 학습용 예시 ​

xplan
[고객 4417 · 거래 12건]
---------------------------------------------------------------------------------------------------------------
| Id  | Operation           | Name               | Starts | E-Rows | A-Rows |   A-Time   | Buffers | Used-Mem |
---------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT    |                    |      1 |        |     12 |00:00:00.01 |     210 |          |
|   1 |  SORT ORDER BY      |                    |      1 |      9 |     12 |00:00:00.01 |     210 | 2048 (0) |
|   2 |   WINDOW SORT       |                    |      1 |      9 |     12 |00:00:00.01 |     210 | 2048 (0) |
|*  3 |    TABLE ACCESS FULL| POINT_TRANSACTIONS |      1 |      9 |     12 |00:00:00.01 |     210 |          |
---------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   3 - filter("CUSTOMER_ID"=:CUS)

[화면 · 위가 최신]
  TX_ID   TX_TYPE  AMOUNT  CREATED_AT            BALANCE
  41207   EARN        320  2024-06-14 13:02:11     1,870
  41206   USE       1,000  2024-06-14 13:02:11     1,870   ← 사용 직후 잔액은 1,550이어야 한다
  38810   EARN        450  2024-05-30 19:44:02     2,550
  …

문제 ​

  1. 사용 줄과 적립 줄의 잔액이 같게 나오는 까닭을 분석 함수의 창(window)으로 설명하시오.
  2. 줄마다 그 거래 직후의 잔액이 나오도록 SQL을 고치고, 화면을 열 때마다 테이블 전체를 읽고 정렬하지 않도록 인덱스를 설계하시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자