ORACLE CBO 的 SQL 自动转换(Cost Based Transformations)之三

ORACLE CBO 的 SQL 自动转换(Cost Based Transformations)之三

咱们来接下来探讨一下 View 和表结合,这时候 CBO 会如何转换用户的 SQL 呢。

通常的一个 View 和表去结合,View 里没什么特殊处理的话,就直接去使用表作 JOIN 即可。

drop table t1 purge;
drop table t2 purge;
create table t1(c1 number, c2 number not null);
create table t2(c1 number, c2 number not null);
insert into t1 values (1,1);
insert into t1 values (1,2);
insert into t1 values (2,2);
insert into t1 values (3,2);
insert into t2 values (1,2);
commit;

SQL> select v1.s2,t2.c2
from t2, (select c1, c2 s2 from t1) v1
where t2.c1=v1.c1;  2    3

        S2         C2
---------- ----------
         1          2
         2          2


Execution Plan
----------------------------------------------------------
Plan hash value: 2959412835

---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |     2 |    24 |     6   (0)| 00:00:01 |
|*  1 |  HASH JOIN         |      |     2 |    24 |     6   (0)| 00:00:01 |
|   2 |   TABLE ACCESS FULL| T2   |     1 |     6 |     3   (0)| 00:00:01 |
|   3 |   TABLE ACCESS FULL| T1   |     2 |    12 |     3   (0)| 00:00:01 |
---------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - access("T2"."C1"="C1")

但是,那要是 VIEW 里面存在 distinct,或者 Group by 的情况下就有点复杂了,View 就没法展开和表去进行结合。
这种情况下,CBO 就会想展开困难的话,那要是把 View 和表结合的谓词下推到 View 中是不是会产生什么神奇效果呢。
于是乎,11g 开始结合谓词下推机能(Join Predicate Pushdown)机能也登场了。

结合谓词下推机能(Join Predicate Pushdown)

首先还是通过下面的例子,看看没用到这个机能的情况下 SQL 执行的情况。

SQL> select v1.s2,t2.c2
from t2, (select c1, sum(c2) s2 from t1 group by c1) v1
where t2.c1=v1.c1;  2    3

        S2         C2
---------- ----------
         3          2


Execution Plan
----------------------------------------------------------
Plan hash value: 1660256317

-----------------------------------------------------------------------------
| Id  | Operation            | Name | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------
|   0 | SELECT STATEMENT     |      |     1 |    32 |     7  (15)| 00:00:01 |
|*  1 |  HASH JOIN           |      |     1 |    32 |     7  (15)| 00:00:01 |
|   2 |   TABLE ACCESS FULL  | T2   |     1 |     6 |     3   (0)| 00:00:01 |
|   3 |   VIEW               |      |     1 |    26 |     4  (25)| 00:00:01 |
|   4 |    HASH GROUP BY     |      |     1 |     6 |     4  (25)| 00:00:01 |
|   5 |     TABLE ACCESS FULL| T1   |     2 |    12 |     3   (0)| 00:00:01 |
-----------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - access("T2"."C1"="V1"."C1")

是不是可以看出,这种情况下,SQL 还是完全按照用户的逻辑来实现的呢。
View 内的查询完成后,结果集和表在去结合,在结合时用到了谓词。

那么,结合谓词下推机能是什么样子呢。

SQL> select /*+ PUSH_PRED(V1) */ v1.s2,t2.c2
from t2, (select c1, sum(c2) s2 from t1 group by c1) v1
where t2.c1=v1.c1;  2    3

        S2         C2
---------- ----------
         3          2


Execution Plan
----------------------------------------------------------
Plan hash value: 3439651121

--------------------------------------------------------------------------------
| Id  | Operation               | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------
|   0 | SELECT STATEMENT        |      |     1 |    19 |     6   (0)| 00:00:01 |
|   1 |  NESTED LOOPS           |      |     1 |    19 |     6   (0)| 00:00:01 |
|   2 |   TABLE ACCESS FULL     | T2   |     1 |     6 |     3   (0)| 00:00:01 |
|   3 |   VIEW PUSHED PREDICATE |      |     1 |    13 |     3   (0)| 00:00:01 |
|*  4 |    FILTER               |      |       |       |            |          |
|   5 |     SORT AGGREGATE      |      |     1 |     6 |            |          |
|*  6 |      TABLE ACCESS FULL  | T1   |     2 |    12 |     3   (0)| 00:00:01 |
--------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   4 - filter(COUNT(*)>0)
   6 - filter("C1"="T2"."C1")

是不是谓词 “C1”=“T2”.“C1” 被下推到了 View 里去了,View 里做了 filter 的结果集再来和表去结合。这个机能被使用时,最大的标识时 VIEW PUSHED PREDICATE 操作。

继续通过 10053 EVENT Trace 再去探究一下,CBO 是怎样实现的。

JPPD: Applying transformation directives
JPPD: Checking validity of push-down in query block SEL$1 (#1)
JPPD: JPPD:   Pushdown from query block SEL$1 (#1) passed validity checks.
JPPD: Performing join predicate push-down (final phase) from query block SEL$1 (#1) to query block SEL$2 (#2)
JPPD:   Pushing predicate "T2"."C1"="V1"."C1"                 <<<<<<<<★
 from query block SEL$1 (#1) to query block SEL$2 (#2)        <<<<<<<<★


Final query after transformations:******* UNPARSED QUERY IS *******
SELECT /*+ PUSH_PRED ("V1") */ "V1"."S2" "S2","T2"."C2" "C2" FROM "U1"."T2" "T2", (SELECT SUM("T1"."C2") "S2" FROM "U1"."T1" "T1" WHERE "T1"."C1"="T2"."C1" HAVING COUNT(*)>0) "V1"


Plan Table
============
-------------------------------------------+-----------------------------------+
| Id  | Operation                | Name    | Rows  | Bytes | Cost  | Time      |
-------------------------------------------+-----------------------------------+
| 0   | SELECT STATEMENT         |         |       |       |     6 |           |
| 1   |  NESTED LOOPS            |         |     1 |    19 |     6 |  00:00:01 |
| 2   |   TABLE ACCESS FULL      | T2      |     1 |     6 |     3 |  00:00:01 |
| 3   |   VIEW PUSHED PREDICATE  |         |     1 |    13 |     3 |  00:00:01 |
| 4   |    FILTER                |         |       |       |       |           |
| 5   |     SORT AGGREGATE       |         |     1 |     6 |       |           |
| 6   |      TABLE ACCESS FULL   | T1      |     2 |    12 |     3 |  00:00:01 |
-------------------------------------------+-----------------------------------+
Query Block Name / Object Alias (identified by operation id):
------------------------------------------------------------
 1 - SEL$1                
 2 - SEL$1                / "T2"@"SEL$1"
 3 - SEL$639F1A6F         / "V1"@"SEL$1"
 4 - SEL$639F1A6F         
 6 - SEL$639F1A6F         / "T1"@"SEL$2"
------------------------------------------------------------
Predicate Information:
----------------------
4 - filter(COUNT(*)>0)
6 - filter("C1"="T2"."C1")

Queryblock “T2”@"SEL$1"的谓词 “T2”.“C1”=“V1”.“C1” 下推到 Queryblock “T1”@“SEL$2”,SQL 在内部被自动转换成下面的样子。

SELECT /*+ PUSH_PRED (“V1”) / “V1”.“S2” “S2”,“T2”.“C2” “C2” FROM “U1”.“T2” “T2”, (SELECT SUM(“T1”.“C2”) “S2” FROM “U1”.“T1” “T1” WHERE “T1”.“C1”=“T2”.“C1” HAVING COUNT()>0) “V1”

上面展示了结合谓词下推机能,这个机能具体是好是坏的评价还要根据实际情况来看,这里就没法过多的展开。
关于如何关闭这个机能,有以下方法:

隐含参数 “_push_join_predicate” 设置成 false

OR

用 /*+ NO_PUSH_PRED */ hint

猜你喜欢

转载自blog.csdn.net/weixin_50513167/article/details/115206759
今日推荐