Skip to content
튜닝 연습PL/SQL과 사용자 정의 함수
PLSQL-033

멤버십 카드 번호로 주문을 찾는 데 한 번에 1.4초, 카드 번호 칸에는 인덱스가 있다

중급DDL버거683 / 700

상황 ​

버거 본사 고객센터와 가맹점주 앱의 「멤버십 적립 내역」은 고객이 불러 주는 카드 번호로 그 카드의 주문을 찾는다. POS 기종마다 카드 번호를 저장하는 모양이 달라서, 작년에 개발팀이 하이픈과 공백을 지우는 함수 FN_NORM_CARD를 만들어 조건에 넣었다. 앱은 입력한 번호에서 숫자만 남겨 :card_no로 넘긴다. 오후 피크에는 이 조회가 한 시간에 1,200번쯤 들어오고, 한 번에 1.4초가 걸리며, AWR의 CPU 1위다. 카드 번호 칸에는 인덱스 ORD_X02가 있는데 쓰이지 않는다.

테이블과 인덱스 ​

  • ORD(이 문제용으로 멤버십 카드 번호 칸 MBR_CARD_NO VARCHAR2(19)를 더한 판): 1,000,000행 · 22,940블록 · ORD_PK (ORD_NO) · ORD_X01 (ORD_DT, ORD_HMS) · ORD_X02 (MBR_CARD_NO) · 통계 최신
  • 카드를 낸 주문은 230,412건(23%)이고 나머지는 MBR_CARD_NO가 NULL이다 · 카드 31,560장 · 카드 한 장의 주문은 평균 7.3건
  • POS 기종 셋이 카드 번호를 '1234-5678-9012-3456', '1234 5678 9012 3456', '1234567890123456' 모양으로 저장한다
  • POS는 영업시간 내내 주문을 넣는다(하루 약 1,370건)

원본 SQL ​

sql
CREATE OR REPLACE FUNCTION FN_NORM_CARD(p_card VARCHAR2) RETURN VARCHAR2 IS
BEGIN
  RETURN REPLACE(REPLACE(p_card, '-'), ' ');         -- 하이픈과 공백을 지운다
END;
/

-- 멤버십 적립 내역 (:card_no = '1234567890123456')
SELECT ORD_NO, ORD_DT, ORD_HMS, SHOP_NO
  FROM ORD
 WHERE FN_NORM_CARD(MBR_CARD_NO) = :card_no
 ORDER BY ORD_DT DESC, ORD_HMS DESC;

실행계획 학습용 예시 ​

xplan
[DISPLAY_CURSOR (ALLSTATS LAST)]
---------------------------------------------------------------------------------------------------
| Id  | Operation          | Name | Starts | E-Rows | A-Rows |   A-Time   | Buffers | Used-Mem |
---------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |      1 |        |      9 |00:00:01.42 |   22940 |          |
|   1 |  SORT ORDER BY     |      |      1 |  10000 |      9 |00:00:01.42 |   22940 | 2048  (0)|
|*  2 |   TABLE ACCESS FULL| ORD  |      1 |  10000 |      9 |00:00:01.42 |   22940 |          |
---------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   2 - filter("DEV"."FN_NORM_CARD"("MBR_CARD_NO")=:CARD_NO)

[V$SQL: 오후 피크 1시간]  EXECUTIONS 1,200 · BUFFER_GETS 27,528,000 · CPU_TIME 1,620 초 · PLSQL_EXEC_TIME 1,320 초
[한 번 평균]              함수 호출 1,000,000번 · CPU 1.35 초 · 경과 1.42 초

문제 ​

  1. 카드 번호 칸에 인덱스가 있는데 테이블 전체를 읽는 이유와, 한 번에 1.35초의 CPU가 드는 이유를 설명하시오.
  2. 하이픈과 공백을 무시하는 규칙은 그대로 두고 인덱스로 그 카드의 주문만 찾도록 함수와 DDL을 고치시오. 인덱스를 만든 뒤 챙길 것과 그 대가도 쓰시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자