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

DETERMINISTIC을 붙였더니 개발자 시험은 빨라졌는데 관리자 화면 내려받기는 그대로다

중급SELECT이커머스658 / 700

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

상황 ​

관리자 화면의 「주문상품 내려받기」는 한 해 치 주문상품을 카테고리명과 함께 파일로 내려 준다. 카테고리명은 함수 fn_category_name이 붙인다. 느리다는 말에 개발자는 블로그 글을 보고 함수에 DETERMINISTIC을 붙였다. "같은 상품이면 한 번만 계산된다고 해서요." 개발자가 SQL*Plus에서 ARRAYSIZE 5000으로 시험했을 때는 함수 본문 실행이 14,697번에서 4,412번으로 줄었다. 그런데 관리자 화면(Java, JDBC)에서 내려받으면 그대로다.

테이블과 인덱스 ​

  • orders: 34,908행 · 193블록 · order_date DATE(시분초 있음) · PK만 · 2024년 주문 6,982건
  • order_items: 73,498행 · 290블록 · PK만 · 2024년 주문의 상품 14,697행 · 그해 팔린 상품 271종
  • products: 280행 · 8블록 · PK(product_id) · category_id
  • categories: 53행 · 4블록 · PK(category_id) · name
  • 관리자 화면은 JDBC의 기본 페치 크기(한 번에 10행)로 받는다 · 통계 최신

원본 SQL ​

sql
CREATE OR REPLACE FUNCTION fn_category_name(p_product_id NUMBER) RETURN VARCHAR2 DETERMINISTIC IS
  v_name categories.name%TYPE;
BEGIN
  SELECT c.name INTO v_name
    FROM products p, categories c
   WHERE p.product_id  = p_product_id
     AND c.category_id = p.category_id;
  RETURN v_name;
END;
/

SELECT o.order_id, o.order_date, oi.product_id, oi.quantity, oi.unit_price,
       fn_category_name(oi.product_id) AS category_name
  FROM orders o, order_items oi
 WHERE o.order_date >= DATE '2024-01-01'
   AND o.order_date <  DATE '2025-01-01'
   AND oi.order_id = o.order_id;

실행계획 학습용 예시 ​

xplan
[관리자 화면에서 내려받을 때: DISPLAY_CURSOR (ALLSTATS LAST)]
-------------------------------------------------------------------------------------------------------
| Id  | Operation          | Name        | Starts | E-Rows | A-Rows |   A-Time   | Buffers | Used-Mem |
-------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |             |      1 |        |  14697 |00:00:00.69 |    1953 |          |
|*  1 |  HASH JOIN         |             |      1 |  14620 |  14697 |00:00:00.69 |    1953 | 1199K (0)|
|*  2 |   TABLE ACCESS FULL| ORDERS      |      1 |   6982 |   6982 |00:00:00.01 |     193 |          |
|   3 |   TABLE ACCESS FULL| ORDER_ITEMS |      1 |  73498 |  73498 |00:00:00.03 |    1760 |          |
-------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   1 - access("OI"."ORDER_ID"="O"."ORDER_ID")
   2 - filter(("O"."ORDER_DATE">=TO_DATE(' 2024-01-01 00:00:00', 'syyyy-mm-dd hh24:mi:ss') AND
              "O"."ORDER_DATE"<TO_DATE(' 2025-01-01 00:00:00', 'syyyy-mm-dd hh24:mi:ss')))
   (ORDER_ITEMS 1,760 = 290블록 + 페치 1,470번마다 다시 잡은 블록)

[함수 본문 실행 수: 함수 안 SELECT의 V$SQL.EXECUTIONS 증가분]
 받는 쪽                          페치 크기   페치 수   DETERMINISTIC 전   DETERMINISTIC 후   경과(후)
 관리자 화면 (JDBC)                    10     1,470             14,697             14,453     0.69 초
 SQL*Plus 시험 (ARRAYSIZE 5000)     5,000         3             14,697              4,412     0.27 초

문제 ​

  1. 같은 함수와 같은 SQL인데 시험에서는 호출이 줄고 관리자 화면에서는 줄지 않은 이유를 설명하시오.
  2. 받는 쪽의 페치 크기와 상관없이 함수 호출이 줄도록 SQL을 고치시오. 함수에 붙인 DETERMINISTIC은 어떻게 할지도 쓰시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자