3.9 JPPD Union View : Union을 사용한 뷰에 조인 조건을 침투시켜라

JPPD

  • Join Predicate Push Down : 뷰 혹은 인라인뷰와 조인을 할 경우에 발생.
JPPD 예제
{code:sqlborderStyle=solid}
SELECT /*+ QB_NAME(OUTER) LEADING(d) USE_NL(e) */
d.department_id, d.department_name, e.employee_id, e.job_id, e.email_phone_num
FROM department d,
(SELECT /*+ QB_NAME(INNER1) */
employee_id, department_id, job_id, email AS email_phone_num
FROM employee
WHERE job_id = :v_job1 --> 'AD_ASST' 대입
UNION
SELECT /*+ QB_NAME(INNER2) */
employee_id, department_id, job_id, phone_number AS email_phone_num
FROM employee
WHERE job_id = :v_job2 ) e --> 'AD_PRES' 대입 )
WHERE d.department_id = e.department_id
AND d.location_id = 1700;















---+







---+

IdOperationNameRowsBytesCostTime















---+







---+

0SELECT STATEMENT128
1NESTED LOOPS955812800:00:02
2TABLE ACCESS BY INDEX ROWIDDEPARTMENT21420200:00:01
3INDEX RANGE SCANDEPT_LOCATION_IX21100:00:01
4VIEW142600:00:01
5SORT UNIQUE252600:00:01
6UNION ALL PUSHED PREDICATE
7TABLE ACCESS BY INDEX ROWIDEMPLOYEE124200:00:01
8INDEX RANGE SCANEMP_JOB_DEPT1100:00:01
9TABLE ACCESS BY INDEX ROWIDEMPLOYEE128200:00:01
10INDEX RANGE SCANEMP_JOB_DEPT1100:00:01















---+







---+
Predicate Information:





--
3 - access("D"."LOCATION_ID"=1700)
8 - access("JOB_ID"=:V_JOB1 AND "DEPARTMENT_ID"="D"."DEPARTMENT_ID")
10 - access("JOB_ID"=:V_JOB2 AND "DEPARTMENT_ID"="D"."DEPARTMENT_ID")

|
||JPPD 예제 - QT가 바꾼 쿼리||
|{code:sql|borderStyle=solid}
SELECT d.department_id, d.department_name, e.employee_id, e.job_id, e.email_phone_num
  FROM department d,
       LATERAL ((SELECT employee_id, department_id, job_id, email AS email_phone_num        								<--
          FROM employee e1
         WHERE job_id = :v_job1 AND e1.department_id = d.department_id												<--
        UNION
        SELECT employee_id, department_id, job_id, phone_number AS email_phone_num
          FROM employee e2
         WHERE job_id = :v_job2 AND e2..department_id = d.department_id  ) e 										<--
 WHERE d.location_id = 1700;

|

JPPD 예제 - 10053 Trace
{code:sql}
.....
***********************************
Cost-Based Join Predicate Push-down
***********************************
JPPD: Checking validity of push-down in query block OUTER (#1)
JPPD: Checking validity of push-down from query block OUTER (#1) to query block SET$1 (#2)
JPPD: Passed validity checks
JPPD: JPPD: Pushdown from query block OUTER (#1) passed validity checks.
Join-Predicate push-down on query block OUTER (#1)
JPPD: Using search type: linear <--
JPPD: Considering join predicate push-down
JPPD: Starting iteration 1, state space = (2) : (0) <--
JPPD: Performing join predicate push-down (no transformation phase) from query block OUTER (#1) to query block SET$1 (#2)

FPD: Considering simple filter push in query block OUTER (#1)
"D"."DEPARTMENT_ID"="E"."DEPARTMENT_ID" AND "D"."LOCATION_ID"=1700
try to generate transitive predicate from check constraints for query block OUTER (#1)
finally: "D"."DEPARTMENT_ID"="E"."DEPARTMENT_ID" AND "D"."LOCATION_ID"=1700 <--

kkqfppRelFilter: Not pushing filter predicates in query block SET$1 (#2) because no predicate to push
FPD: Considering simple filter push in query block INNER2 (#3)
"EMPLOYEE"."JOB_ID"=:B1
try to generate transitive predicate from check constraints for query block INNER2 (#3)
finally: "EMPLOYEE"."JOB_ID"=:B1

FPD: Considering simple filter push in query block INNER1 (#4)
"EMPLOYEE"."JOB_ID"=:B1
try to generate transitive predicate from check constraints for query block INNER1 (#4)
finally: "EMPLOYEE"."JOB_ID"=:B1

JPPD: Costing transformed query. <--

.............

*********************************
Number of join permutations tried: 1
*********************************
(newjo-save) 1 0
Trying or-Expansion on query block OUTER (#1)
Transfer Optimizer annotations for query block OUTER (#1)
Final cost for query block OUTER (#1) - All Rows Plan:
Best join order: 1
Cost: 128.0356 Degree: 1 Card: 9.0000 Bytes: 603
Resc: 128.0356 Resc_io: 86.0000 Resc_cpu: 931127367
Resp: 128.0356 Resp_io: 86.0000 Resc_cpu: 931127367
kkoqbc-subheap (delete addr=0x0ECE3F28, in-use=19436, alloc=22500)
kkoqbc-end:
:
call(in-use=45668, alloc=81864), compile(in-use=169652, alloc=181084), execution(in-use=4444, alloc=8136)

kkoqbc: finish optimizing query block OUTER (#1)
CBQT: Saved costed qb# 4 (INNER1), key = INNER1_00000000_0
CBQT: Saved costed qb# 3 (INNER2), key = INNER2_00000000_0
CBQT: Saved costed qb# 2 (SET$1), key = SET$1_00004200_2
CBQT: Saved costed qb# 1 (OUTER), key = OUTER_00000000_0
JPPD: Updated best state, Cost = 128.04 <-- iteration 1의 cost는 128.04
JPPD: Starting iteration 2, state space = (2) : (1) <-- 곧바로 iteration 2 진행
JPPD: Performing join predicate push-down (candidate phase) from query block OUTER (#1) to query block SET$1 (#2)
JPPD: Pushing predicate "D"."DEPARTMENT_ID"="E"."DEPARTMENT_ID" <-- JPPD를 수행하기 위해 조인 조건을
from query block OUTER (#1) to query block SET$1 (#2) <-- 쿼리블럭 SET$01에 침투

..............

Registered qb: SEL$4E984A90 0xee7dfc4 (PUSHED PREDICATE INNER1) <-- JPPD가 수행되어 새로운 쿼리블럭 생성






-
QUERY BLOCK SIGNATURE





-
signature (): qb_name=SEL$4E984A90 nbfros=1 flg=0
fro(0): flg=0 objn=74263 hint_alias="EMPLOYEE"@"INNER1"

Registered qb: SET$5715CE2E 0xee31d98 (PUSHED PREDICATE SET$1; OUTER; "E"@"OUTER" 2) <-- JPPD의 결과로 SET$1의 쿼리블럭명이 변경






-
QUERY BLOCK SIGNATURE





-
signature (): qb_name=SET$5715CE2E nbfros=1 flg=0
fro(0): flg=0 objn=0 hint_alias="NULL_HALIAS"@"SET$5715CE2E"

Registered qb: SEL$FAA94C03 0xee32274 (PUSHED PREDICATE INNER2) <-- JPPD가 수행되어 새로운 쿼리블럭 생성






-
QUERY BLOCK SIGNATURE





-
signature (): qb_name=SEL$FAA94C03 nbfros=1 flg=0
fro(0): flg=0 objn=74263 hint_alias="EMPLOYEE"@"INNER2"

.............

FPD: Considering simple filter push in query block OUTER (#1)
"D"."LOCATION_ID"=1700
try to generate transitive predicate from check constraints for query block OUTER (#1)
finally: "D"."LOCATION_ID"=1700

kkqfppRelFilter: Not pushing filter predicates in query block SET$5715CE2E (#2) because no predicate to push
FPD: Considering simple filter push in query block SEL$FAA94C03 (#3)
"EMPLOYEE"."JOB_ID"=:B1 AND :B2="EMPLOYEE"."DEPARTMENT_ID"
try to generate transitive predicate from check constraints for query block SEL$FAA94C03 (#3)
finally: "EMPLOYEE"."JOB_ID"=:B1 AND "EMPLOYEE"."DEPARTMENT_ID"=:B2 <-- 각 쿼리블럭에 FPD가 발생하여 조인 조건을 새로 생성된 쿼리블럭들에 생성

FPD: Considering simple filter push in query block SEL$4E984A90 (#4)
"EMPLOYEE"."JOB_ID"=:B1 AND :B2="EMPLOYEE"."DEPARTMENT_ID"
try to generate transitive predicate from check constraints for query block SEL$4E984A90 (#4)
finally: "EMPLOYEE"."JOB_ID"=:B1 AND "EMPLOYEE"."DEPARTMENT_ID"=:B2 <-- 각 쿼리블럭에 FPD가 발생하여 조인 조건을 새로 생성된 쿼리블럭들에 생성

............

JPPD: Updated best state, Cost = 128.03 <-- JPPD costing 결과 128.03
JPPD: Will use JPPD from OUTER (#1) to SET$1 (#2). <-- costr가 저렴하므로 JPPD 선택

kkqctdrvTD-cleanup: transform(in-use=43284, alloc=45368) :
call(in-use=89124, alloc=114616), compile(in-use=303688, alloc=307188), execution(in-use=4936, alloc=8136)

kkqctdrvTD-end:
call(in-use=89124, alloc=114616), compile(in-use=258264, alloc=307188), execution(in-use=4936, alloc=8136)

JPPD: Applying transformation directives
JPPD: Checking validity of push-down in query block OUTER (#1)
JPPD: JPPD: Pushdown from query block OUTER (#1) passed validity checks.
JPPD: Performing join predicate push-down (final phase) from query block OUTER (#1) to query block SET$1 (#2)
JPPD: Pushing predicate "D"."DEPARTMENT_ID"="E"."DEPARTMENT_ID" <--
from query block OUTER (#1) to query block SET$1 (#2)

JPPD: Push dest of pred 0x0EC67598 is qb 0x0E511DC0:query block SET$1 (#2)
JPPD: Push dest of pred 0x0EC6B648 is qb 0x0E511DC0:query block SET$1 (#2)

query block OUTER (#1) unchanged
FPD: Considering simple filter push in query block OUTER (#1)
"D"."LOCATION_ID"=1700
try to generate transitive predicate from check constraints for query block OUTER (#1)
finally: "D"."LOCATION_ID"=1700

kkqfppRelFilter: Not pushing filter predicates in query block SET$5715CE2E (#2) because no predicate to push
FPD: Considering simple filter push in query block SEL$FAA94C03 (#3)
"EMPLOYEE"."JOB_ID"=:B1 AND :B2="EMPLOYEE"."DEPARTMENT_ID"
try to generate transitive predicate from check constraints for query block SEL$FAA94C03 (#3)
finally: "EMPLOYEE"."JOB_ID"=:B1 AND "EMPLOYEE"."DEPARTMENT_ID"=:B2

FPD: Considering simple filter push in query block SEL$4E984A90 (#4)
"EMPLOYEE"."JOB_ID"=:B1 AND :B2="EMPLOYEE"."DEPARTMENT_ID"
try to generate transitive predicate from check constraints for query block SEL$4E984A90 (#4)
finally: "EMPLOYEE"."JOB_ID"=:B1 AND "EMPLOYEE"."DEPARTMENT_ID"=:B2

|
* JPPD + UNION 변환을 control하는 파라미터는 _push_join_union_view2 (default true)
* PUSH_PRED / NO_PUSHE_PRED 힌트 사용.
* JPPD의 수행 여부를 cost로서 판단하게 만드는 파라미터는 _optimizer_push_pred_cost_based (default true)
** 이 파라미터가 flase 되어 있다고 해도 JPPD가 수행될 수 있다.
** 이 기능을 OJPPD(old-style (non-cost-based) JPPD)라고 부르며 OJPPD가 수행되지 않을 때는 OLD_PUSH_PRED 힌트 사용.

|{code:sql|borderStyle=solid}
SELECT /*+ QB_NAME(OUTER) LEADING(d) USE_NL(e) OLD_PUSH_PRED(@OUTER E@OUTER (EMPLOYEE.DEPARTMENT_ID)) */
       d.department_id, d.department_name, e.employee_id, e.job_id, e.email_phone_num
  FROM department d,
       (SELECT /*+ QB_NAME(INNER1) */
               employee_id, department_id, job_id, email AS email_phone_num
          FROM employee
         WHERE job_id = :v_job1          --> 'AD_ASST' 대입
        UNION
        SELECT /*+ QB_NAME(INNER2) */
               employee_id, department_id, job_id, phone_number AS email_phone_num
          FROM employee
         WHERE job_id = :v_job2   ) e      --> 'AD_PRES' 대입   ) 
 WHERE d.department_id = e.department_id 
   AND d.location_id = 1700;
   
-----------------------------------------------------------+-----------------------------------+
| Id  | Operation                        | Name            | Rows  | Bytes | Cost  | Time      |
-----------------------------------------------------------+-----------------------------------+
| 0   | SELECT STATEMENT                 |                 |       |       |    11 |           |
| 1   |  NESTED LOOPS                    |                 |    11 |   506 |    11 |  00:00:01 |
| 2   |   TABLE ACCESS BY INDEX ROWID    | DEPARTMENT      |    21 |   420 |     2 |  00:00:01 |
| 3   |    INDEX RANGE SCAN              | DEPT_LOCATION_IX|    21 |       |     1 |  00:00:01 |
| 4   |   VIEW                           |                 |     1 |    26 |     1 |  00:00:01 |
| 5   |    SORT UNIQUE                   |                 |       |       |       |           |
| 6   |     UNION-ALL PARTITION          |                 |       |       |       |           |
| 7   |      TABLE ACCESS BY INDEX ROWID | EMPLOYEE        |     1 |    24 |     2 |  00:00:01 |
| 8   |       INDEX RANGE SCAN           | EMP_JOB_DEPT    |     1 |       |     1 |  00:00:01 |
| 9   |      TABLE ACCESS BY INDEX ROWID | EMPLOYEE        |     1 |    28 |     2 |  00:00:01 |
| 10  |       INDEX RANGE SCAN           | EMP_JOB_DEPT    |     1 |       |     1 |  00:00:01 |
-----------------------------------------------------------+-----------------------------------+
Predicate Information:
----------------------
3 - access("D"."LOCATION_ID"=1700)
8 - access("JOB_ID"=:V_JOB1 AND "DEPARTMENT_ID"="D"."DEPARTMENT_ID")
10 - access("JOB_ID"=:V_JOB2 AND "DEPARTMENT_ID"="D"."DEPARTMENT_ID")

-- Plan상에서는 알 수 없지만, Predicate information의 id 8,10을 보면 JPPD가 성공적으로 수행되었다.


OJPPD: Performing join predicate push-down in union view 											<--
 from query block SET$1 (#0) to query block INNER2 (#0)												<--
OJPPD: Pushing predicate "D"."DEPARTMENT_ID"="E"."DEPARTMENT_ID"

OJPPD: Used promoted index: EMP_DEPARTMENT_IX
Registered qb: SET$AD7CC163 0x102d17bc (OLD PUSHED PREDICATE SET$1; OUTER; "E"@"OUTER")
---------------------
QUERY BLOCK SIGNATURE
---------------------
  signature (): qb_name=SET$AD7CC163 nbfros=1 flg=0
    fro(0): flg=0 objn=0 hint_alias="NULL_HALIAS"@"SET$AD7CC163"

Registered qb: SEL$AC386DA3 0x102d24ac (OLD PUSHED PREDICATE INNER2)
---------------------
QUERY BLOCK SIGNATURE
---------------------
  signature (): qb_name=SEL$AC386DA3 nbfros=1 flg=0
    fro(0): flg=0 objn=74263 hint_alias="EMPLOYEE"@"INNER2"

OJPPD: Performing join predicate push-down in union view 											<--
 from query block SET$AD7CC163 (#0) to query block INNER1 (#0)										<--
OJPPD: Pushing predicate "D"."DEPARTMENT_ID"="E"."DEPARTMENT_ID"
-- OJPPD가 쿼리블럭 INNER1과 INNER2에 수행
-- Non-Cost0-Based-JPPD이므로 search type이나 iteration과정은 존재하지 않는다.

|