Skip to Main Content
  • Questions
  • Oracle Undocumented Parameter - _PUSH_JOIN_PREDICATE

Breadcrumb

Question and Answer

Connor McDonald

Thanks for the question, Arnab.

Asked: June 21, 2018 - 5:39 am UTC

Last updated: June 27, 2018 - 1:04 am UTC

Version: Oracle Database Version 11.2.0.3.0

Viewed 10K+ times! This question is

You Asked

Hi,

I have a long standing question regarding one of the undocumented parameters of Oracle namely "_push_join_predicate".

I personally experienced scenarios where a query (involving multiple big joins) would show up - "Pushed Predicate" in the plan and would take long hours to execute. To get rid of this problem I changed the parameter "_push_join_predicate" to false. The result was immediate, the query which was taking long hours to execute was now executing within a few minutes.

Sorry, that I do not have the query with me now but the question that I have is this:-

It is principally correct to use this parameter as long as it is seen to be solving matters(going against the general view that using undocumented parameters are not recommended) OR shun the use of these undocumented parameters always and one should change the query constructs to avoid running into this problem.


Thanks
Arnab

and we said...

Normally, a view cannot be joined with an index-based nested loop join, since a view can't have an index defined on it. Join predicate pushdown (JPPD) is an optimizer query transformation that allows a view to be joined with an index-based nested-loop join method, which may provide a more optimal execution plan. More information on this transformation can be found on the Optimizer blog https://blogs.oracle.com/optimizer/optimizer-transformation:-join-predicate-pushdown

With this transformation, the view remains a separate query block, but it now contains the join predicate, which is pushed down from its containing query block into the view. The view thus becomes correlated and must be evaluated for each row of the outer query block. These pushed-down join predicates, once inside the view, open up new index access paths on the base tables inside the view; this allows the view to be joined with index-based nested-loop join method, thereby enabling the optimizer to select an efficient execution plan.


Take the following query for example,
SELECT s.order_id, V.prod_id
FROM   sales s,
       (SELECT p.prod_id, p.prod_name, p.prod_desc
        FROM   products p, product_details pd
        WHERE  p.prod_category_id = pd.prod_category_id
       ) V
WHERE  s.prod_id = V.prod_id(+) 
AND    s.prod_name = V.prod_name(+) 
AND    s.cust_id = 5;


Without Join predicate push down the execution plan is

----------------------------------------------------------------------------------
| Id  | Operation        | Name        | Rows  | Cost   |
----------------------------------------------------------------------------------
|   0 | SELECT STATEMENT       |         |     5 |    15 |
|*  1 |  HASH JOIN OUTER       |         |     5 |    15 |
|   2 |   TABLE ACCESS BY INDEX ROWID BATCHED| T4        |    15 |     2  | 
|*  3 |    INDEX RANGE SCAN       | T4_IND_THOUSAND |       |     1  | 
|   4 |   VIEW         |         |  5000 |    13  | 
|   5 |    NESTED LOOPS        |         |  5000 |    13 | 
|   6 |     TABLE ACCESS FULL       | T10        | 10000 |    12 |
|*  7 |     INDEX UNIQUE SCAN       | T5_IND_U3       |     4 |     0  | 
-----------------------------------------------------------------------------------


With join predicate push down the query is rewritten to be as follows (note this isn’t real SQL):
SELECT s.order_id, V.prod_id
FROM   sales s,
       (SELECT p.prod_id, p.prod_name, p.prod_desc
        FROM   products p, product_details pd
        WHERE  p.prod_category_id = pd.prod_category_id
        AND    s.prod_id = V.prod_id(+)
        AND    s.prod_name = V.prod_name(+))V
WHERE  s.cust_id = 5; 

Allowing the execution plan to become

---------------------------------------------------------------------------------------
| Id  | Operation          | Name   | Rows  | Cost (%CPU)|
---------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT         |   |     5 |    12   (0)|
|   1 |  NESTED LOOPS OUTER         |   |     5 |    12   (0)|
|   2 |   TABLE ACCESS BY INDEX ROWID BATCHED  | SALES   |     1 |     2   (0)|
|*  3 |    INDEX RANGE SCAN         | SALES_IND_PROD  |     1 |     1   (0)| 
|   4 |   VIEW PUSHED PREDICATE         |   |     1 |    10   (0)|
|*  5 |    HASH JOIN          |   |    10 |    10   (0)|
|   6 |     TABLE ACCESS BY INDEX ROWID BATCHED| PRODUCTS  |    10 |     5   (0)|
|   7 |      BITMAP CONVERSION TO ROWIDS       |   |  |       |
|   8 |       BITMAP AND         |   |  |       |
|   9 |        BITMAP CONVERSION FROM ROWIDS   |   |  |       |
|* 10 |  INDEX RANGE SCAN        | PROD_IND_ID     |  |     1   (0)|
|  11 |        BITMAP CONVERSION FROM ROWIDS   |   |  |       |
|* 12 |  INDEX RANGE SCAN        | T10_IND_TEN  |  |     2   (0)| 
|  13 |     INDEX UNIQUE SCAN         | PD_IND_CAT  |  5000 |     5   (0)|
---------------------------------------------------------------------------------------

The join predicate pushdown transformation is not always optimal. With join predicate pushed-down the view becomes correlated and it must be evaluated for each row in the outer query; if there is a large number of outer rows, the cost of evaluating the view multiple times may make the nested-loop join suboptimal that is why the optimizer will cost the plan with and without the transformation and pick the plan with the lowest cost.

Based on your explanation it sounds like the optimizer is in correctly selecting to use the transformation. This is typically caused by a cardinality misestimate, where the optimizer estimates that the number of outer rows is less than it really is. This can happen for number of reason, everything for stale or missing statistics to correlation between the column used in the query. Correcting the cardinality misestimate typically enables the optimizer to make the right decision and not use the transformation.

Setting the underscore parameter disables the JPPD transformation altogether and is effectively fixing the problem at the wrong scope. After all join predicate push down is not a problem for every query on the system but you are disabling it for every query on the system. If you can’t fix the problem by correcting the cardinality mis-estimate then I would suggest you log a bug and correct the problem at the scope of the problem, in other words at the statement level.

You can disable the join predicate push transformation via the NO_PUSH_PRED hint, which you can add to the statement directly or via a SQL plan baseline, profile or SQL patch. You can find more information on how to do that in this blog post https://blogs.oracle.com/optimizer/using-sql-patch-to-add-hints-to-a-packaged-application

Rating

  (1 rating)

Is this answer out of date? If it is, please let us know via a Comment

Comments

Great ...

Arnab Panja, June 26, 2018 - 7:01 am UTC

Thanks for the useful reply. You have explained more than I thought of. This fully answers my question.

Thanks
~Arnab
Connor McDonald
June 27, 2018 - 1:04 am UTC

Glad we could help

More to Explore

Performance

Get all the information about database performance in the Database Performance guide.