포스트

Join Predicate Push Down (JPPD) 는 Group By 가 있는 View 도 침투 가능 (2)

외부 GROUP BY + 인라인 뷰 GROUP BY 의 2단 집계 패턴에서도 JPPD 가 동작합니다. ORDERS 의 20 행이 인라인 뷰 안으로 한 행씩 밀려 들어가 SORT GROUP BY 가 부서당 124 행만 다루도록 압축되는 예시입니다.

Join Predicate Push Down (JPPD) 는 Group By 가 있는 View 도 침투 가능 (2)

GROUP BY 가 있는 인라인 뷰는 물론, VIEW 내부에 DISTINCT 가 있어도 JPPD 가 동작합니다. 옵티마이저는 외부 join 술어를 뷰 안으로 밀어 넣어 집계 대상 행 수를 줄입니다.

핵심 정리

  1. GROUP BY 가 있는 view 에도 view 바깥의 조인 조건을 view 내부로 침투시킬 수 있습니다.
  2. 이런 식으로 VIEW 내부에 DISTINCT 가 있어도 JPPD 가 가능합니다.

쿼리

ORDERS (A) + 인라인 뷰 B (ORDER_ITEMS · PRODUCTS 의 GROUP BY) 를 NL 로 묶고, 외부에서 다시 GROUP BY 로 일별 / 상품명별 집계를 만드는 패턴입니다. 인라인 뷰 안의 B.ORDER_ID 가 외부 조인 키라는 점을 주목해야 합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
SELECT /*+ LEADING(A B) NO_PUSH_PRED(B) */
       TO_CHAR(A.ORDER_DATE, 'YYYYMMDD') ORDER_DATE
     , B.PRODUCT_NAME
     , SUM(B.SALES_AMT) SALES_AMT
  FROM ORDERS A,
      (SELECT /*+ NO_MERGE LEADING(B C) USE_NL(C) */
              B.ORDER_ID, C.PRODUCT_NAME
            , SUM(B.UNIT_PRICE*B.QUANTITY) SALES_AMT
         FROM ORDER_ITEMS B,
              PRODUCTS C
        WHERE B.PRODUCT_ID = C.PRODUCT_ID
        GROUP BY B.ORDER_ID, C.PRODUCT_NAME) B    -- ← JPPD JOIN 조건절에 대한 GROUP BY
 WHERE A.ORDER_ID = B.ORDER_ID
   AND A.ORDER_DATE >= TO_DATE('20120701', 'YYYYMMDD')
   AND A.ORDER_DATE <  TO_DATE('20120701', 'YYYYMMDD')
   AND A.EMPLOYEE_ID = 'E070'
 GROUP BY TO_CHAR(A.ORDER_DATE, 'YYYYMMDD'), B.PRODUCT_ID;

실행 계획

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
-------------------------------------------------------------------------------------------------------
| Id | Operation                          | Name              | Starts | A-Rows | Buffers | Used-Mem  |
-------------------------------------------------------------------------------------------------------
|  0 | SELECT STATEMENT                   |                   |      1 |    121 |     313 |           |
|  1 |  HASH GROUP BY                     |                   |      1 |    121 |     313 | 1270K (0) |
|  2 |   NESTED LOOPS                     |                   |      1 |    142 |     313 |           |
|  3 |    TABLE ACCESS BY INDEX ROWID     | ORDERS            |      1 |     20 |      22 |           |
|* 4 |     INDEX RANGE SCAN               | IX_ORDERS_N2      |      1 |     20 |       3 |           |
|  5 |    VIEW PUSHED PREDICATE           |                   |     20 |    124 |     291 |           |
|  6 |     SORT GROUP BY                  |                   |     20 |    124 |     291 |  2048 (0) |
|  7 |      NESTED LOOPS                  |                   |     20 |    124 |     291 |           |
|  8 |       NESTED LOOPS                 |                   |     20 |    124 |     167 |           |
|  9 |        TABLE ACCESS BY INDEX ROWID | ORDER_ITEMS       |     20 |    124 |     163 |           |
|*10 |         INDEX RANGE SCAN           | IX_ORDER_ITEMS_PK |     20 |    124 |      42 |           |
|*11 |        INDEX UNIQUE SCAN           | IX_PRODUCTS_PK    |    124 |    124 |       4 |           |
| 12 |       TABLE ACCESS BY INDEX ROWID  | PRODUCTS          |    124 |    124 |     124 |           |
-------------------------------------------------------------------------------------------------------

Id 5 의 VIEW PUSHED PREDICATE 가 핵심 포인트입니다. 외부 NL (Id 2) 의 안쪽 가지인 인라인 뷰 B 가 ORDERS 의 각 row (20 회 Starts) 마다 호출되며, 그때마다 외부 술어 A.ORDER_ID = B.ORDER_ID 가 뷰 안의 SORT GROUP BY (Id 6) 위로 밀려 들어가 부서당 필요한 124 행만 집계됩니다.


참조

외부 join 키와 일치하는 컬럼이 인라인 뷰의 GROUP BY 키에 있으면, JPPD 후 그 컬럼은 상수처럼 작동합니다.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.