接下来,让我们增加更多的行数到表T2来看看子光标们能增长到多大:
SQL> delete t2;
5555 rows deleted.
SQL> insert into t2 select rownum from dual connect by level <= 99999;
99999 rows created.
SQL> select a from t1 where a in (select /*+ PRECOMPUTE_SUBQUERY */b from t2);
A
----------
1
2
3
4
5
6
7
8
9
9 rows selected.
SQL> @hash
HASH_VALUE SQL_ID CHILD_NUMBER
---------- ------------- ------------
4073976606 5phajvmtd7wsy 2
SQL> @curheaps 4073976606 2
KGLNAHSH KGLHDPAR CHILD# KGLHDADR KGLOBHD0 SIZE0 SIZE1 SIZE2 SIZE3 SIZE4 SIZE5 KGLOBHD6 SIZE6 SIZE7
---------- -------- ---------- -------- -------- -------- -------- -------- -------- -------- -------- -------- -------- -------- --
4073976606 3175A618 2 3343C208 2FD9768C 3392 0 0 0 0 0 312B45D8 925236 0
HEAP CLASS ALLOC_COMMENT BYTES CHUNKS
----- -------- ---------------- ---------- ----------
HEAP0 perm permanent memor 2500 4
HEAP0 free free memory 412 2
HEAP0 freeabl kgltbtab 228 3
HEAP CLASS ALLOC_COMMENT BYTES CHUNKS
----- -------- ---------------- ---------- ----------
HEAP6 freeabl optdef: qcopCre 720288 10000
HEAP6 freeabl strdef_buf : kk 196476 10001
HEAP6 freeabl kctdef : kkqucu 1460 5
HEAP6 freeabl kctdef : qcdlgo 876 3
HEAP6 freeabl ctxdef:kksLoadC 488 1
HEAP6 free free memory 456 1
[..snip..删减]
HEAP6 freeabl opixfalo:froaty 16 1
53 rows selected.
看,似乎所允许的过滤条件的最大数目是10000(这个测试是在11.1.0.7 on Linux上做的)。并且不是所有的内存结构都有10000+ 桶(chunks) 填充的。似乎是在解析过程中,Oracle认识到有太多的过滤条件存放在内存中了,因此它抽离出来,从precompute_subquery函数中返回了失败。这就导致precompute_subquery Hint被忽略了,生成的执行计划仅仅好似没有该hint 存在一样:
----------------------------------------------------------------------------昂
| Id | Operation | Name | E-Rows | OMem | 1Mem | Used-Mem |
----------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | | |
|* 1 | HASH JOIN SEMI | | 1 | 1066K| 1066K| 1084K (0)|
| 2 | TABLE ACCESS FULL | T1 | 9 | | | |
| 3 | VIEW | VW_NSO_1 | 9 | | | |
| 4 | TABLE ACCESS FULL| T2 | 9 | | | |
----------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("A"="B")
这应该是很明显的, precompute_subquery hint 没有被真正忽略掉。优化器确实增加了它,但是在它认识到所有的条件和规则不是都满足外,它抽离出来(这应该很好的示例说明了这个事实,有效的hints永远不会被忽略—他们会被处理,但是有时其他的输入条件使得优化器终止使用它)。注意这个hint 是一种特殊的情况,它恰好击中了一种内部硬编码限制,基于真实处理的行数,而不是一个基于成本的判断。
另外一个直接的问题应该是,当top-level 查询被重新执行时(当它已经被缓存在library cache中的时候),Oracle 总是触发对表T2的递归子查询.如果不是这样,我们可能得终止使用缓存在 heap6中的错误的过滤条件,如果T2的内容发生变化的话。
答案是,不论何时Oracle 在不同的上下文中运行一个查询的部分内容,它都将会把top-level 查询打上标记,其后续的执行将不能共享光标规则,并最终总是导致一个硬解析发生。从我的hash.sql脚本输出内容里面,就是你在上面所看到的,child_number在每次执行里面都是增加的。
让我们用nonshared.sql脚本(其仅仅查询v$sql_shared_cursor视图,但是格式化输出得更好一些)检查一下,没有共享光标的原因。这个脚本要使用SQL_ID作为参数:
SQL> @nonshared 5phajvmtd7wsy
SQL_ID : 5phajvmtd7wsy
ADDRESS : 3175A618
CHILD_ADDRESS : 3175B56C
CHILD_NUMBER : 0
-----------------
SQL_ID : 5phajvmtd7wsy
ADDRESS : 3175A618
CHILD_ADDRESS : 2FD2D6DC
CHILD_NUMBER : 1
CURSOR_PARTS_MISMATCH : Y
-----------------
SQL_ID : 5phajvmtd7wsy
ADDRESS : 3175A618
CHILD_ADDRESS : 3343C208
CHILD_NUMBER : 2
CURSOR_PARTS_MISMATCH : Y
-----------------
PL/SQL procedure successfully completed.
Oracle documentation 关于CURSOR_PARTS_MISMATCH说明如下:
“Cursor was compiled with subexecution (cursor parts were executed)”
光标被编译用子执行(被执行的光标部分)
所以,Oracle现在可以从主查询中摘取子查询的文本,分别执行它们,然后将执行完的结果返回给主查询作为“硬编码”的过滤值。
似乎这个特性仅仅被用于Oracle OLAP选项,对于常规的SQL不自动地使用。原因是在Oracle里面,没有足够有效的“半连接”(semijoin)操作可应用到OLAP 数据上,因此,这种优化(在我的每个简短的测试中,subquery precomputing的执行效率实际上都比一个哈希半连接要差)。
最后,这个hint 是没有文档说明的(可能一般认为是对OLAP而言的) ,所以请不要试图使用它来优化你的查询!:)
作者的sql脚本我没下载。
但是得出的结论是不要轻易使用该hint,我的应用版本也比较低,放弃了,记录在此!
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/7177735/viewspace-708013/ ,如需转载,请注明出处,否则将追究法律责任。