`

Oracle Predicate Pushing(原创)

 
阅读更多

Introduction
The join predicate pushdown (JPPD) transformation allows a view to be joined with index-based nested-loop join method, which may provide a more optimal alternative. In the join predicate pushdown transformation, the view remains a separate query block, but it 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.
 The join predicate pushdown transformation is not always optimal. The join predicate pushed-down view becomes correlated and it must be evaluated for each outer row; if there is a large number of outer rows, the cost of evaluating the view multiple times may make the nested-loop join suboptimal, and therefore joining the view with hash or sort-merge join method may be more efficient.
The decision whether to push down join predicates into a view is determined by evaluating the costs of the outer query with and without the join predicate pushdown transformation under Oracle's cost-based query transformation framework. The join predicate pushdown transformation applies to both non-mergeable views and mergeable views and to pre-defined and inline views as well as to views generated internally by the optimizer during various transformations.

The following shows the types of views on which join predicate pushdown is currently supported.

  •     UNION ALL/UNION view
  •     Outer-joined view
  •     Anti-joined view
  •     Semi-joined view
  •     DISTINCT view
  •     GROUP-BY view

Prerequisite

Predicate pushing did not happen by default in Oracle 8i. In 8i the hidden parameter "_push_join_predicate" defaults to false whereas it defaults to true in 9i, 10G and 11G.
Predicate pushing is part from query transformations. Query transformation was based on heuristic rules in 9i. From 10G R1 however by default query transformations are cost based since "_optimizer_cost_based_transformation" defaults to linear. ( there are bugs related to cost based query transformation causing huge performance degradation )
In the below example I try to show what predicate pushing is and what the impact at explain plan level is.
Assume we setup the sample schema

Sample
If we run the below SQL we notice that -- at least in our testcase --- Predicate pushing did happens automatically, however we can hint it with the PUSH_PRED hint
select /*+ GATHER_PLAN_STATISTICS PUSH_PRED(inner_view) */ su_pk,su_name,su_comment,inner_view.maxamount from
t_supplier_su,
( select max(or_totalamount) maxamount,su_fk from t_order_or group by su_fk ) inner_view
where t_supplier_su.su_pk = inner_view.su_fk(+) and t_supplier_su.su_name is not null;
SQL> select * from table(dbms_xplan.display_cursor(null,null,'ADVANCED'));
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
SQL_ID    gxxa8g94dzk7n, child number 0
-------------------------------------
select /*+ GATHER_PLAN_STATISTICS PUSH_PRED(inner_view) */
su_pk,su_name,su_comment,inner_view.maxamount from t_supplier_su, (
select max(or_totalamount) maxamount,su_fk from t_order_or group by
su_fk ) inner_view where t_supplier_su.su_pk = inner_view.su_fk(+) and
t_supplier_su.su_name is not null
Plan hash value: 3140464201
-------------------------------------------------------------------------------------------
| Id  | Operation                | Name          | Rows    | Bytes | Cost (%CPU)| Time    |
-------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT         |               |         |       |   690K(100)|          |
|   1 |  NESTED LOOPS OUTER      |               |     100 | 21800 |   690K  (1)| 02:18:01 |
|*  2 |   TABLE ACCESS FULL      | T_SUPPLIER_SU |     100 | 20300 |     3   (0)| 00:00:01 |
|   3 |   VIEW PUSHED PREDICATE  |               |       1 |    15 |  6900   (1)| 00:01:23 |
|   4 |    SORT GROUP BY         |               |       1 |     9 |  6900   (1)| 00:01:23 |
|*  5 |     TABLE ACCESS FULL    | T_ORDER_OR    |   10101 | 90909 |  6900   (1)| 00:01:23 |
-------------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
   1 - SEL$1
   2 - SEL$1        / T_SUPPLIER_SU@SEL$1
   3 - SEL$639F1A6F / INNER_VIEW@SEL$1
   4 - SEL$639F1A6F
   5 - SEL$639F1A6F / T_ORDER_OR@SEL$2
Outline Data
-------------
/*+
   BEGIN_OUTLINE_DATA
   IGNORE_OPTIM_EMBEDDED_HINTS
   OPTIMIZER_FEATURES_ENABLE('11.2.0.2')
   DB_VERSION('11.2.0.2')
   ALL_ROWS
   OUTLINE_LEAF(@"SEL$639F1A6F")
   PUSH_PRED(@"SEL$1" "INNER_VIEW"@"SEL$1" 1)
   OUTLINE_LEAF(@"SEL$1")
   OUTLINE(@"SEL$2")
   OUTLINE(@"SEL$1")
   FULL(@"SEL$1" "T_SUPPLIER_SU"@"SEL$1")
   NO_ACCESS(@"SEL$1" "INNER_VIEW"@"SEL$1")
   LEADING(@"SEL$1" "T_SUPPLIER_SU"@"SEL$1" "INNER_VIEW"@"SEL$1")
   USE_NL(@"SEL$1" "INNER_VIEW"@"SEL$1")
   FULL(@"SEL$639F1A6F" "T_ORDER_OR"@"SEL$2")
   USE_HASH_AGGREGATION(@"SEL$639F1A6F")
   END_OUTLINE_DATA
*/
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("T_SUPPLIER_SU"."SU_NAME" IS NOT NULL)
5 - filter("SU_FK"="T_SUPPLIER_SU"."SU_PK")
Column Projection Information (identified by operation id):
-----------------------------------------------------------
1 - "T_SUPPLIER_SU"."SU_PK"[NUMBER,22],
  ...................................................................
71 rows selected.
If we run the below SQL we notice that we can avoid predicate pushing (at any cost) with the NO_PUSH_PRED hint.
select /*+ GATHER_PLAN_STATISTICS NO_PUSH_PRED(inner_view) */ su_pk,su_name,su_comment,inner_view.currency,inner_view.maxamount from
t_supplier_su,
( select max(or_totalamount) maxamount,su_fk,cr_fk currency from t_order_or group by su_fk,cr_fk ) inner_view
where t_supplier_su.su_pk = inner_view.su_fk(+) and t_supplier_su.su_name is not null;
SQL> select * from table(dbms_xplan.display_cursor(null,null,'ADVANCED'));
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
SQL_ID    0gbc9khzuznzk, child number 0
-------------------------------------
select /*+ GATHER_PLAN_STATISTICS NO_PUSH_PRED(inner_view) */
su_pk,su_name,su_comment,inner_view.currency,inner_view.maxamount from
t_supplier_su, ( select max(or_totalamount) maxamount,su_fk,cr_fk
currency from t_order_or group by su_fk,cr_fk ) inner_view where
t_supplier_su.su_pk = inner_view.su_fk(+) and t_supplier_su.su_name is
not null
Plan hash value: 83112093
----------------------------------------------------------------------------------------
| Id  | Operation             | Name          | Rows  | Bytes | Cost  (%CPU)| Time     |
----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |               |       |       |  6954  (100)|          |
|*  1 |  HASH JOIN RIGHT OUTER|               |   281 | 66597 |  6954    (2)| 00:01:24 |
|   2 |   VIEW                |               |   281 |  9554 |  6950    (2)| 00:01:24 |
|   3 |    HASH GROUP BY      |               |   281 |  3653 |  6950    (2)| 00:01:24 |
|   4 |     TABLE ACCESS FULL | T_ORDER_OR    |  1000K|    12M|  6905    (1)| 00:01:23 |
|*  5 |   TABLE ACCESS FULL   | T_SUPPLIER_SU |   100 | 20300 |     3    (0)| 00:00:01 |
----------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
    1 - SEL$1
    2 - SEL$2 / INNER_VIEW@SEL$1
    3 - SEL$2
    4 - SEL$2 / T_ORDER_OR@SEL$2
    5 - SEL$1 / T_SUPPLIER_SU@SEL$1
Outline Data
-------------
/*+
    BEGIN_OUTLINE_DATA
    IGNORE_OPTIM_EMBEDDED_HINTS
    OPTIMIZER_FEATURES_ENABLE('11.2.0.2')
    DB_VERSION('11.2.0.2')
    ALL_ROWS
    OUTLINE_LEAF(@"SEL$2")
    OUTLINE_LEAF(@"SEL$1")
    FULL(@"SEL$1" "T_SUPPLIER_SU"@"SEL$1")
    NO_ACCESS(@"SEL$1" "INNER_VIEW"@"SEL$1")
    LEADING(@"SEL$1" "T_SUPPLIER_SU"@"SEL$1" "INNER_VIEW"@"SEL$1")
    USE_HASH(@"SEL$1" "INNER_VIEW"@"SEL$1")
    SWAP_JOIN_INPUTS(@"SEL$1" "INNER_VIEW"@"SEL$1")
    FULL(@"SEL$2" "T_ORDER_OR"@"SEL$2")
    USE_HASH_AGGREGATION(@"SEL$2")
    END_OUTLINE_DATA
*/
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("T_SUPPLIER_SU"."SU_PK"="INNER_VIEW"."SU_FK")
5 - filter("T_SUPPLIER_SU"."SU_NAME" IS NOT NULL)
Column Projection Information (identified by operation id):
-----------------------------------------------------------
1 - (#keys=1) "T_SUPPLIER_SU"."SU_PK"[NUMBER,22],
  ...................................................................
"T_SUPPLIER_SU"."SU_NAME"[VARCHAR2,400], "SU_COMMENT"[VARCHAR2,400]
71 rows selected.
Predicate pushing becomes more interesting when the inner view cannot be merged because there is an outer join. If the filtering predicate is selectif predicate pushing can then "push this filter down" into the inner view.
select /*+ GATHER_PLAN_STATISTICS PUSH_PRED(inner_view) */ su_pk,su_name,su_comment,inner_view.currency,inner_view.maxamount from
t_supplier_su,
( select max(or_totalamount) maxamount,su_fk,cr_fk currency from t_order_or group by su_fk,cr_fk ) inner_view
where t_supplier_su.su_pk = inner_view.su_fk(+) and t_supplier_su.su_pk= 1;
SQL> select * from table(dbms_xplan.display_cursor(null,null,'ADVANCED'));
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
SQL_ID    bw56zj2jw7vng, child number 0
-------------------------------------
select /*+ GATHER_PLAN_STATISTICS PUSH_PRED(inner_view) */
su_pk,su_name,su_comment,inner_view.currency,inner_view.maxamount from
t_supplier_su, ( select max(or_totalamount) maxamount,su_fk,cr_fk
currency from t_order_or group by su_fk,cr_fk ) inner_view where
t_supplier_su.su_pk = inner_view.su_fk(+) and t_supplier_su.su_pk= 1
Plan hash value: 484793169
-----------------------------------------------------------------------------------------------------
| Id  | Operation                      | Name          |   Rows  |   Bytes |   Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT               |               |         |         |    6902 (100)|          |
|   1 |  NESTED LOOPS OUTER            |               |       3 |     678 |    6902   (1)| 00:01:23 |
|   2 |   TABLE ACCESS BY INDEX ROWID  | T_SUPPLIER_SU |       1 |     203 |       1   (0)| 00:00:01 |
|*  3 |    INDEX UNIQUE SCAN           | SU_PK         |       1 |         |       0   (0)|          |
|   4 |   VIEW PUSHED PREDICATE        |               |       3 |      69 |    6901   (1)| 00:01:23 |
|   5 |    SORT GROUP BY               |               |       3 |      39 |    6901   (1)| 00:01:23 |
|*  6 |     FILTER                     |               |         |         |              |          |
|*  7 |      TABLE ACCESS FULL         | T_ORDER_OR    |   11636 |     147K|    6900   (1)| 00:01:23 |
-----------------------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
    1 - SEL$1
    2 - SEL$1        / T_SUPPLIER_SU@SEL$1
    3 - SEL$1        / T_SUPPLIER_SU@SEL$1
    4 - SEL$639F1A6F / INNER_VIEW@SEL$1
    5 - SEL$639F1A6F
    7 - SEL$639F1A6F / T_ORDER_OR@SEL$2
Outline Data
-------------
/*+
    BEGIN_OUTLINE_DATA
    IGNORE_OPTIM_EMBEDDED_HINTS
    OPTIMIZER_FEATURES_ENABLE('11.2.0.2')
    DB_VERSION('11.2.0.2')
    ALL_ROWS
    OUTLINE_LEAF(@"SEL$639F1A6F")
    PUSH_PRED(@"SEL$1" "INNER_VIEW"@"SEL$1" 1)
    OUTLINE_LEAF(@"SEL$1")
    OUTLINE(@"SEL$2")
    OUTLINE(@"SEL$1")
    INDEX_RS_ASC(@"SEL$1" "T_SUPPLIER_SU"@"SEL$1" ("T_SUPPLIER_SU"."SU_PK"))
    NO_ACCESS(@"SEL$1" "INNER_VIEW"@"SEL$1")
    LEADING(@"SEL$1" "T_SUPPLIER_SU"@"SEL$1" "INNER_VIEW"@"SEL$1")
    USE_NL(@"SEL$1" "INNER_VIEW"@"SEL$1")
    FULL(@"SEL$639F1A6F" "T_ORDER_OR"@"SEL$2")
    USE_HASH_AGGREGATION(@"SEL$639F1A6F")
    END_OUTLINE_DATA
*/
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("T_SUPPLIER_SU"."SU_PK"=1)
6 - filter(1="T_SUPPLIER_SU"."SU_PK")
7 - filter("SU_FK"=1)
Column Projection Information (identified by operation id):
-----------------------------------------------------------
1 - "T_SUPPLIER_SU"."SU_PK"[NUMBER,22], "SU_NAME"[VARCHAR2,400],
  ...................................................................
77 rows selected.
Another example of interesting predicate pushing is the example below. For the supplier 100 there are no orders (read no records in the inner view).
We need however an entry in the result set for supplier 100, thus we need an outer join.
The filtering predicate results in the usage of the index OR_IDX1, we only need (selectif) records for 1 supplier. ( supplier 1 )
select /*+ GATHER_PLAN_STATISTICS NO_MERGE(inner_view) PUSH_PRED(inner_view) */ su_pk,su_name,su_comment,inner_view.currency,inner_view.maxamount from
t_supplier_su,
( select max(or_totalamount) maxamount,su_fk,cr_fk currency from t_order_or group by su_fk,cr_fk ) inner_view
where t_supplier_su.su_pk = inner_view.su_fk(+) and t_supplier_su.su_pk in (1,100);
SU_PK
----------
SU_NAME
--------------------------------------------------------------------------------
SU_COMMENT
--------------------------------------------------------------------------------
CURRENCY      MAXAMOUNT
------------ ----------
1
ZAQMGJLUHAZPEVKNGPXEWNYVKUPXMDENWSGCWAFRBRJMNABHOLVHESPKVFKTMQIESGRBVPFIHHRKOZCZ
  ...................................................................
We love SQL tuning
SQL> select * from table(dbms_xplan.display_cursor(null,null,'ADVANCED'));
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
SQL_ID    997w5gw1rvgdu, child number 0
-------------------------------------
select /*+ GATHER_PLAN_STATISTICS NO_MERGE(inner_view)
PUSH_PRED(inner_view) */ su_pk,su_name,su_comment,inner_view.currency,in
ner_view.maxamount from t_supplier_su, ( select max(or_totalamount)
maxamount,su_fk,cr_fk currency from t_order_or group by su_fk,cr_fk )
inner_view where t_supplier_su.su_pk = inner_view.su_fk(+) and
t_supplier_su.su_pk in (1,100)
Plan hash value: 331405424
----------------------------------------------------------------------------------------------------
| Id  | Operation                         | Name          | Rows    | Bytes | Cost (%CPU)|  Time    |
-----------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                  |               |         |       |    299(100)|          |
|   1 |  NESTED LOOPS OUTER               |               |       2 |   452 |    299  (1)| 00:00:04 |
|   2 |   INLIST ITERATOR                 |               |         |       |            |          |
|   3 |    TABLE ACCESS BY INDEX ROWID    | T_SUPPLIER_SU |       2 |   406 |      3  (0)| 00:00:01 |
|*  4 |     INDEX UNIQUE SCAN             | SU_PK         |       2 |       |      1  (0)| 00:00:01 |
|   5 |   VIEW PUSHED PREDICATE           |               |       1 |    23 |    148  (1)| 00:00:02 |
|   6 |    SORT GROUP BY                  |               |       6 |    78 |    148  (1)| 00:00:02 |
|*  7 |     FILTER                        |               |         |       |            |          |
|   8 |      TABLE ACCESS BY INDEX ROWID  | T_ORDER_OR    |     151 |  1963 |    147  (0)| 00:00:02 |
|*  9 |       INDEX RANGE SCAN            | OR_IDX1       |     151 |       |     22  (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
    1 - SEL$1
    3 - SEL$1        / T_SUPPLIER_SU@SEL$1
    4 - SEL$1        / T_SUPPLIER_SU@SEL$1
    5 - SEL$639F1A6F / INNER_VIEW@SEL$1
    6 - SEL$639F1A6F
    8 - SEL$639F1A6F / T_ORDER_OR@SEL$2
    9 - SEL$639F1A6F / T_ORDER_OR@SEL$2
Outline Data
-------------
/*+
    BEGIN_OUTLINE_DATA
    IGNORE_OPTIM_EMBEDDED_HINTS
    OPTIMIZER_FEATURES_ENABLE('11.2.0.2')
    DB_VERSION('11.2.0.2')
    ALL_ROWS
    OUTLINE_LEAF(@"SEL$639F1A6F")
    PUSH_PRED(@"SEL$1" "INNER_VIEW"@"SEL$1" 1)
    OUTLINE_LEAF(@"SEL$1")
    OUTLINE(@"SEL$2")
    OUTLINE(@"SEL$1")
    INDEX_RS_ASC(@"SEL$1" "T_SUPPLIER_SU"@"SEL$1" ("T_SUPPLIER_SU"."SU_PK"))
    NO_ACCESS(@"SEL$1" "INNER_VIEW"@"SEL$1")
    LEADING(@"SEL$1" "T_SUPPLIER_SU"@"SEL$1" "INNER_VIEW"@"SEL$1")
    USE_NL(@"SEL$1" "INNER_VIEW"@"SEL$1")
    INDEX_RS_ASC(@"SEL$639F1A6F" "T_ORDER_OR"@"SEL$2" ("T_ORDER_OR"."SU_FK"))
    USE_HASH_AGGREGATION(@"SEL$639F1A6F")
    END_OUTLINE_DATA
*/
Predicate Information (identified by operation id):
---------------------------------------------------
4 - access(("T_SUPPLIER_SU"."SU_PK"=1 OR "T_SUPPLIER_SU"."SU_PK"=100))
7 - filter((1="T_SUPPLIER_SU"."SU_PK" OR 100="T_SUPPLIER_SU"."SU_PK"))
9 - access("SU_FK"="T_SUPPLIER_SU"."SU_PK")
filter(("SU_FK"=1 OR "SU_FK"=100))
Column Projection Information (identified by operation id):
-----------------------------------------------------------
1 - "T_SUPPLIER_SU"."SU_PK"[NUMBER,22], "SU_NAME"[VARCHAR2,400],
  ...................................................................
9 - "T_ORDER_OR".ROWID[ROWID,10], "SU_FK"[NUMBER,22]
85 rows selected.Consider another example of a query , which contains a view with the UNION ALL set operator.

select  /*+ push_pred(v) */  prod.* from PROMOTIONS prod, (
select p.PROD_ID,AMOUNT_SOLD,s.PROMO_ID from sales s, PRODUCTS p
where s.prod_id = p.prod_id
union all
select p.PROD_ID,UNIT_PRICE,c.PROMO_ID from COSTS c, PRODUCTS p
where c.prod_id = p.prod_id) V
where prod.PROMO_ID=v.PROMO_ID;
select * from TABLE(dbms_xplan.display_cursor(NULL, NULL, 'ADVANCED'));

Plan hash value: 1442795797
 
-------------------------------------------------------------------------------------------------------------
| Id  | Operation                     | Name        | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
-------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |             |  1000K|    97M|   284K  (2)| 00:56:58 |       |       |
|   1 |  NESTED LOOPS                 |             |  1000K|    97M|   284K  (2)| 00:56:58 |       |       |
|   2 |   TABLE ACCESS FULL           | PROMOTIONS  |   503 | 49294 |    17   (0)| 00:00:01 |       |       |
|   3 |   VIEW                        |             |   511 |  2044 |   566   (2)| 00:00:07 |       |       |
|   4 |    UNION ALL PUSHED PREDICATE |             |       |       |            |          |       |       |
|*  5 |     HASH JOIN                 |             |   229K|  2691K|   492   (3)| 00:00:06 |       |       |
|   6 |      INDEX FULL SCAN          | PRODUCTS_PK |    72 |   288 |     1   (0)| 00:00:01 |       |       |
|   7 |      PARTITION RANGE ALL      |             |   229K|  1794K|   489   (2)| 00:00:06 |     1 |    28 |
|*  8 |       TABLE ACCESS FULL       | SALES       |   229K|  1794K|   489   (2)| 00:00:06 |     1 |    28 |
|   9 |     NESTED LOOPS              |             | 27371 |   320K|    75   (3)| 00:00:01 |       |       |
|  10 |      PARTITION RANGE ALL      |             | 27371 |   213K|    74   (2)| 00:00:01 |     1 |    28 |
|* 11 |       TABLE ACCESS FULL       | COSTS       | 27371 |   213K|    74   (2)| 00:00:01 |     1 |    28 |
|* 12 |      INDEX UNIQUE SCAN        | PRODUCTS_PK |     1 |     4 |     0   (0)| 00:00:01 |       |       |
-------------------------------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
   1 - SEL$1       
   2 - SEL$1        / PROD@SEL$1
   3 - SET$5715CE2E / V@SEL$1
   4 - SET$5715CE2E
   5 - SEL$639F1A6F
   6 - SEL$639F1A6F / P@SEL$2
   8 - SEL$639F1A6F / S@SEL$2
   9 - SEL$B01C6807
  11 - SEL$B01C6807 / C@SEL$3
  12 - SEL$B01C6807 / P@SEL$3
Outline Data
-------------
  /*+
      BEGIN_OUTLINE_DATA
      PX_JOIN_FILTER(@"SEL$639F1A6F" "S"@"SEL$2")
      USE_HASH(@"SEL$639F1A6F" "S"@"SEL$2")
      LEADING(@"SEL$639F1A6F" "P"@"SEL$2" "S"@"SEL$2")
      FULL(@"SEL$639F1A6F" "S"@"SEL$2")
      INDEX(@"SEL$639F1A6F" "P"@"SEL$2" ("PRODUCTS"."PROD_ID"))
      USE_NL(@"SEL$B01C6807" "P"@"SEL$3")
      LEADING(@"SEL$B01C6807" "C"@"SEL$3" "P"@"SEL$3")
      INDEX(@"SEL$B01C6807" "P"@"SEL$3" ("PRODUCTS"."PROD_ID"))
      FULL(@"SEL$B01C6807" "C"@"SEL$3")
      USE_NL(@"SEL$1" "V"@"SEL$1")
      LEADING(@"SEL$1" "PROD"@"SEL$1" "V"@"SEL$1")
      NO_ACCESS(@"SEL$1" "V"@"SEL$1")
      FULL(@"SEL$1" "PROD"@"SEL$1")
      OUTLINE(@"SEL$1")
      OUTLINE(@"SET$1")
      OUTLINE(@"SEL$3")
      OUTLINE(@"SEL$2")
      OUTLINE_LEAF(@"SEL$1")
      PUSH_PRED(@"SEL$1" "V"@"SEL$1" 1)
      OUTLINE_LEAF(@"SET$5715CE2E")
      OUTLINE_LEAF(@"SEL$B01C6807")
      OUTLINE_LEAF(@"SEL$639F1A6F")
      ALL_ROWS
      DB_VERSION('11.2.0.1')
      OPTIMIZER_FEATURES_ENABLE('11.2.0.1')
      IGNORE_OPTIM_EMBEDDED_HINTS
      END_OUTLINE_DATA
  */
Predicate Information (identified by operation id):
---------------------------------------------------
   5 - access("S"."PROD_ID"="P"."PROD_ID")
   8 - filter("S"."PROMO_ID"="PROD"."PROMO_ID")
  11 - filter("C"."PROMO_ID"="PROD"."PROMO_ID")
  12 - access("C"."PROD_ID"="P"."PROD_ID")
Column Projection Information (identified by operation id):
-----------------------------------------------------------
   1 - (#keys=0) "PROD"."PROMO_ID"[NUMBER,22], "PROD"."PROMO_NAME"[VARCHAR2,30],
    
  ...................................................................
  11 - "C"."PROD_ID"[NUMBER,22], "C"."PROMO_ID"[NUMBER,22]

the above, 'VIEW UNION ALL PUSHED PREDICATE' indicates that the UNION ALL view has undergone the join predicate pushdown transformation. As can be seen, here the join predicate has been replicated and pushed inside every branch of the UNION ALL view. The join predicates (shown here in red) open up index access paths thereby enabling index-based nested loop join of the view.
Consider following query as an example of join predicate pushdown into a distinct view. We have the following cardinalities of the tables involved in this query : Sales (1,016,271), Customers (50,000), and Costs (787,766).

SELECT C.cust_last_name, C.cust_city
FROM customers C,
           (SELECT DISTINCT S.cust_id
            FROM sales S, costs CT
            WHERE S.prod_id = CT.prod_id and CT.unit_price > 70) V
WHERE C.cust_state_province = 'CA' and C.cust_id = V.cust_id;
Plan hash value: 3834618923
------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                             | Name           | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                      |                |    49 |  1764 | 49900   (2)| 00:09:59 |       |       |
|   1 |  NESTED LOOPS SEMI                    |                |    49 |  1764 | 49900   (2)| 00:09:59 |       |       |
|*  2 |   TABLE ACCESS FULL                   | CUSTOMERS      |   383 | 13022 |   405   (1)| 00:00:05 |       |       |
|   3 |   VIEW PUSHED PREDICATE               |                | 18205 | 36410 |   129   (2)| 00:00:02 |       |       |
|*  4 |    HASH JOIN                          |                |   143K|  2515K|   129   (2)| 00:00:02 |       |       |
|   5 |     PARTITION RANGE ALL               |                |   130 |  1170 |    54   (0)| 00:00:01 |     1 |    28 |
|   6 |      TABLE ACCESS BY LOCAL INDEX ROWID| SALES          |   130 |  1170 |    54   (0)| 00:00:01 |     1 |    28 |
|   7 |       BITMAP CONVERSION TO ROWIDS     |                |       |       |            |          |       |       |
|*  8 |        BITMAP INDEX SINGLE VALUE      | SALES_CUST_BIX |       |       |            |          |     1 |    28 |
|   9 |     PARTITION RANGE ALL               |                | 79172 |   695K|    74   (2)| 00:00:01 |     1 |    28 |
|* 10 |      TABLE ACCESS FULL                | COSTS          | 79172 |   695K|    74   (2)| 00:00:01 |     1 |    28 |
------------------------------------------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
   1 - SEL$1      
   2 - SEL$1        / C@SEL$1
   3 - SEL$639F1A6F / V@SEL$1
   4 - SEL$639F1A6F
   6 - SEL$639F1A6F / S@SEL$2
  10 - SEL$639F1A6F / CT@SEL$2
Outline Data
-------------
  /*+
      BEGIN_OUTLINE_DATA
      PX_JOIN_FILTER(@"SEL$639F1A6F" "CT"@"SEL$2")
      USE_HASH(@"SEL$639F1A6F" "CT"@"SEL$2")
      LEADING(@"SEL$639F1A6F" "S"@"SEL$2" "CT"@"SEL$2")
      FULL(@"SEL$639F1A6F" "CT"@"SEL$2")
      BITMAP_TREE(@"SEL$639F1A6F" "S"@"SEL$2" AND(("SALES"."CUST_ID")))
      USE_NL(@"SEL$1" "V"@"SEL$1")
      LEADING(@"SEL$1" "C"@"SEL$1" "V"@"SEL$1")
      NO_ACCESS(@"SEL$1" "V"@"SEL$1")
      FULL(@"SEL$1" "C"@"SEL$1")
      OUTLINE(@"SEL$1")
      OUTLINE(@"SEL$2")
      OUTLINE_LEAF(@"SEL$1")
      PUSH_PRED(@"SEL$1" "V"@"SEL$1" 3)
      OUTLINE_LEAF(@"SEL$639F1A6F")
      ALL_ROWS
      DB_VERSION('11.2.0.1')
      OPTIMIZER_FEATURES_ENABLE('11.2.0.1')
      IGNORE_OPTIM_EMBEDDED_HINTS
      END_OUTLINE_DATA
  */
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - filter("C"."CUST_STATE_PROVINCE"='CA')
   4 - access("S"."PROD_ID"="CT"."PROD_ID")
   8 - access("S"."CUST_ID"="C"."CUST_ID")
  10 - filter("CT"."UNIT_PRICE">70)
Column Projection Information (identified by operation id):
-----------------------------------------------------------
   1 - (#keys=0) "C"."CUST_LAST_NAME"[VARCHAR2,40], "C"."CUST_CITY"[VARCHAR2,30]
  ...................................................................
  10 - "CT"."PROD_ID"[NUMBER,22]
As shown above, when query D undergoes join predicate pushdown transformation, the expensive DISTINCT operator is removed and the join is converted into a semi-join; this is possible, since all the SELECT list items of the view participate in an equi-join with the outer tables. Under similar conditions, when a group-by view undergoes join predicate pushdown transformation, the expensive group-by operator can also be removed.
With the join predicate pushdown transformation, the elapsed time of query D came down from 63 seconds to 5 seconds.
Since distinct and group-by views are mergeable views, the cost-based transformation framework also compares the cost of merging the view with that of join predicate pushdown in selecting the most optimal execution plan.
Remember we can avoid that any query transformation ahppens with the NO_QUERY_TRANSFORMATION hint.
SQL> select /*+ GATHER_PLAN_STATISTICS NO_QUERY_TRANSFORMATION */ su_pk,su_name,su_comment,inner_view.currency,
2  inner_view.maxamount from t_supplier_su, ( select max(or_totalamount)
maxamount,su_fk,cr_fk currency from t_order_or group by su_fk,cr_fk )
inner_view where t_supplier_su.su_pk = inner_view.su_fk(+) and
t_supplier_su.su_pk in (1,100) ;
Tuning case

早 上某数据库服务器CPU不断报警,应用系统管理员同时反馈应用响应明显变慢。登陆数据库主机查看,应用连接上来的几个进程占用了大量的CPU资源,造成 CPU空闲率很低。登陆数据库查询,发现有不少buffer cache chains的等待,初步判断是应用上出现了某些性能糟糕的SQL语句。通过进程捕获了几条耗资源的SQL语句,发现大部分都是类似同一条语句造成的。手 工执行一下,需要2分多钟才能出结果。捕获到的SQL语句如下:
SELECT * 
FROM (SELECT DISTINCT e.n_event_id, 
e.n_parent_id,  e.v_event_num,  em.n_req_type_1,  em.n_req_type_2,  em.v_title,  em.v_description,  e.n_priority,  cb.n_time_limit,  cb.n_status,  e.n_process_way,  e.n_maintaince_event_id,  e.v_maintaince_flag,  e.v_replacedevice_flag,  et.d_acbept_date,  et.d_finish_date,  et.v_exempt_status,  et.n_callback_status,  et.n_delay_time,  erpl.n_creator,  erpl.d_creation_date,  e.n_req_id,  el.v_res_notice_msg,  el.v_res_notice_email,  el.v_res_notice_im,  vd.v_valid_status,  vd.v_related_org_id,  e.n_dev_process_way,  e.v_over_time_reason,  e.v_confirm_person,  e.v_new_device_num 
FROM tb_event e,  tb_event_related_person erpl,  vorg_department vd,  tb_callback cb,  tb_event_log el,  tb_event_marker em,  tb_event_track et 
WHERE e.n_event_id = et.n_event_id(+)  AND e.n_event_id = em.n_event_id(+)  AND e.n_event_id = el.n_event_id(+)  AND e.n_event_id = cb.n_event_id(+)  AND erpl.n_dept_id = vd.recid(+)  AND e.n_event_id = erpl.n_event_id(+) 
ORDER BY e.d_creation_date DESC) 
WHERE rownum <= 40; 
vorg_department为一个view,具体定义如下: 

create or replace view vorg_department as 
select d.recid,  r.v_valid_status,  d.v_related_org_id,  r.org_parent,  r.tree_no,  d.dept_kind,  d.dept_type,  d.dept_name,  d.status,  d.dept_code,  d.area_code,  d.dept_prof,  d.sort_num,  d.link_addr,  r.layer 
from ORG_DEPARTMENT d, ORG_DEPRELATION r 
where d.recid = r.org_child 
and r.relation_type = 1 
该语句的执行计划如下: 
PLAN_TABLE_OUTPUT 
———————————————————————————————————–  
| Id  | Operation                     |  Name                 | Rows  | Bytes|TempSpc|Cost|  
———————————————————————————————————–  
|   0 | SELECT STATEMENT              |                       |     40| 15404|    |  23M|  
|*  1 |  COUNT STOPKEY                |                       |       |      |    |     |  
|   2 |   VIEW                        |                       |   110M|  395G|    |  23M|  
|*  3 |    SORT UNIQUE STOPKEY        |                       |   110M|   27G| 58G|  11M|  
|   4 |     NESTED LOOPS OUTER        |                       |   110M|   27G|    | 2797|  
|*  5 |      HASH JOIN OUTER          |                       |  1255K|  308M| 39M| 2797|  
|*  6 |       HASH JOIN OUTER         |                       |   153K|   37M| 28M| 1873|  
|*  7 |        HASH JOIN OUTER        |                       |   115K|   27M| 24M| 1384|  
|*  8 |         HASH JOIN OUTER       |                       |   101K|   23M| 23M|  990|  
|*  9 |          HASH JOIN OUTER      |                       |   100K|   22M| 17M|  571|  
|  10 |           TABLE ACCESS FULL   | TB_EVENT              |  77044|   16M|    |  256|  
|  11 |           INDEX FAST FULL SCAN| IDX_EVENT_TRACK_N_E_ID|   100K|  491K|    |    4|  
|  12 |          INDEX FAST FULL SCAN |IDX_TB_CALLBAK_E_ID    |  75959|  296K|    |   21|  
|  13 |         TABLE ACCESS FULL     | TB_EVENT_MARKER       |   3686| 18430|    |    3|  
|* 14 |        INDEX FAST FULL SCAN   | IDX_TB_EVENT_RP_DUP1  |   101K|  895K|    |    4|  
|  15 |       INDEX FAST FULL SCAN    |IDX_TB_EVENT_LOG_N_E_ID|   628K| 2455K|    |  149|  
|  16 |      VIEW PUSHED PREDICATE    | VORG_DEPARTMENT       |     88| 1144 |    |     |  
|  17 |       NESTED LOOPS            |                       |     1 |   15 |    |    2|  
|* 18 |        INDEX UNIQUE SCAN    | PK_DEPARTMENT           |     1 |     6|    |    1|  
|* 19 |        INDEX RANGE SCAN     | ASSOCIATION8_FK         |     1 |     9|    |    1| 
———————————————————————————————————–  
首 先了解了一下各表的统计信息情况和表的数据情况,统计信息都是最新的,除了tb_event_log表略微大点,有60多万数据外,其余表均10万左右或 更小,执行计划里面的小表table full sacn和一些索引的access情况也并无严重问题。接下来关注表与表之间的关联顺序和方式。继续分析这个执行计划,第四行评估出了一个超级大的结果 集,找到这个结果集的产生方式,第16行引起了我的注意。VIEW PUSHED PREDICATE,在存在out join和view的情况下,CBO自动选择了谓词推进到了视图VORG_DEPARTMENT中以过滤更多的数据,也算无可厚非。  但继续往上找,找到这个谓词的基数,第5行,发现评估出来的基数竟然有100多万行之多,而视图本身的结果集,只有不到3000行!看来这是一个“愚蠢 的”执行计划,CBO先选择视图之外的表做关联,tb_event虽然本身很小,但由于与其他表做多次外连接, 因此最终得到一个庞大的基数,而拿着这个结果集推进到视图中,想想是多么恐怖的事情!一般来说,大集合的结果集合并不适合进行PUSHED PREDICATE。如果只是少量,会起到比较好的效果。
既然知道了原因,那就尝试着去改变这个执行计划。Oracle提供了no_push_pred和push_pred来改变pushed predication行为,在上述语句中添加no_push_pred提示:  
SELECT *  
FROM (SELECT /*+ no_push_pred(vd) */ DISTINCT e.n_event_id,  
e.n_parent_id,  
e.v_event_num,  
….  
FROM tb_event e,  
tb_event_related_person erpl,  
vorg_department vd,  
tb_callback cb,  
tb_event_log el,  
tb_event_marker em,  
tb_event_track et  
WHERE e.n_event_id = et.n_event_id(+)  
AND e.n_event_id = em.n_event_id(+)  
AND e.n_event_id = el.n_event_id(+)  
AND e.n_event_id = cb.n_event_id(+)  
AND erpl.n_dept_id = vd.recid(+)  
AND e.n_event_id = erpl.n_event_id(+)  
ORDER BY e.d_creation_date DESC)  
WHERE rownum <= 40;  
修改后的执行计划如下:  
———————————————————————————————————–  
| Id  |                     Operation |                   Name | Rows | Bytes|TempSpc|Cost|
———————————————————————————————————–  
|   0 | SELECT STATEMENT              |                        |   40 | 11553|     |  181K|
|*  1 |  COUNT STOPKEY                |                        |      |      |     |      |
|   2 |   VIEW                        |                        | 1470K| 5398M|     |  181K|
|*  3 |    SORT UNIQUE STOPKEY        |                        | 1470K|  370M| 765M| 92546|
|*  4 |     HASH JOIN OUTER           |                        | 1470K|  370M|  46M|  3546|
|*  5 |      HASH JOIN OUTER          |                        |  180K|   44M|  39M|  2499|
|*  6 |       HASH JOIN OUTER         |                        |  153K|   37M|  28M|  1873|
|*  7 |        HASH JOIN OUTER        |                        |  115K|   27M|  24M|  1384|
|*  8 |         HASH JOIN OUTER       |                        |  101K|   23M|  23M|   990|
|*  9 |          HASH JOIN OUTER      |                        |  100K|   22M|  17M|   571|
|  10 |           TABLE ACCESS FULL   | TB_EVENT               | 77044|   16M|     |   256|
|  11 |           INDEX FAST FULL SCAN| IDX_EVENT_TRACK_N_E_ID |  100K|  491K|     |     4|
|  12 |          INDEX FAST FULL SCAN | IDX_TB_CALLBAK_E_ID    | 75959|  296K|     |    21|
|  13 |         TABLE ACCESS FULL     | TB_EVENT_MARKER        |  3686| 18430|     |     3|
|* 14 |        INDEX FAST FULL SCAN   | IDX_TB_EVENT_RP_DUP1   |  101K|  895K|     |     4|
|  15 |       VIEW                    | VORG_DEPARTMENT        |  3355| 20130|     |     3|
|  16 |        NESTED LOOPS           |                        |  3355| 50325|     |     3|
|* 17 |         INDEX FAST FULL SCAN  | ASSOCIATION8_FK        |  3356| 30204|     |     3|
|* 18 |         INDEX UNIQUE SCAN     | PK_DEPARTMENT          |     1|     6|     |      |
|  19 |      INDEX FAST FULL SCAN     | IDX_TB_EVENT_LOG_N_E_ID|  628K| 2455K|     |   149|
———————————————————————————————————  
表tb_event e和tb_event_track et关联 通过e 和 tb_callback cb关联 通过 e和 tb_event_marker em,
调 整后整个执行速度有了明显提升,15秒之内可以返回结果,可以看到view结果集与第六步产生的结果集进行hash join outer,然后得到的结果集合与最大的表tb_event_log再次进行hash join outer。当然这个执行计划可能还有完善的余地,需进一步根据数据情况调整表的关联顺序.

参考至:http://www.online-database.eu/index.php/sql-tuning/180-predicate-pushing

             https://blogs.oracle.com/optimizer/entry/basics_of_join_predicate_pushdown_in_oracle
             http://blog.csdn.net/zhaoyangjian724/article/details/17121707

本文原创,转载请注明出处、作者

如有错误,欢迎指正

邮箱:czmcj@163.com

0
0
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics