♣题目 部分
在Oracle中,如何得到真实的执行计划?
♣答案部分
在Oracle数据库中判断得到的执行计划是否准确,就是看目标SQL是否被真正执行过,真正执行过的SQL所对应的执行计划就是准确的,反之则有可能不准,因此,通过10046事件及如下的几种方式得到的执行计划是最准确的,而从其它方式获取到的执行计划都有可能不准确。
1SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(NULL, NULL, 'ADVANCED ALLSTATS'));2SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR('sql_id/hash_value',CHILD_CURSOR_NUMBER, 'ADVANCED ALLSTATS'));3SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_AWR('sql_id'));SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(NULL, NULL, 'ADVANCED ALLSTATS'));
2SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR('sql_id/hash_value',CHILD_CURSOR_NUMBER, 'ADVANCED ALLSTATS'));
3SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_AWR('sql_id'));
这里需要注意的是,虽然SQL*Plus的AUTOTRACE功能有部分是真实执行了SQL语句的(例如所有DML语句),但是,由于该命令所显示的执行计划来源于调用EXPLAIN PLAN命令,所以,其得到的执行计划依然可能不准确(特别是在使用了绑定变量的情况下)。那么,为什么EXPLAIN PLAN命令里显示的预估执行计划与该SQL真实的执行计划不一样呢?原因有多个方面,常见的情况包括以下几个方面:
① 绑定变量窥视(Bind Peeking):EXPLAIN PLAN里不会进行绑定变量窥视,但是Runtime Plan里会进行绑定变量窥视,所以,如果发生这种情况,那么会使这两个执行计划产生差异。
② 隐式转换:Explain Plan里不会考虑绑定变量的类型,但是Runtime Plan里会考虑类型,从而有可能会根据绑定变量的类型出现隐式转换,所以谓词(Predicate)会发生变化,使得执行计划也会产生差异。
③ 优化器参数:执行Explain Plan的Session与Runtime Plan的Session不是同一个。如果各个Session之间存在优化器参数差异,那么执行计划也会产生差异。
④ 统计信息收集参数:Explain Plan始终是用最新的统计信息产生执行计划,但是,Runtime Plan不一定会用最新的统计信息。因此也会产生执行计划差异。在收集统计信息时,一个与缓存的游标是否失效的很重要的参数为NO_INVALIDATE。在重新收集统计信息时,可以指定NO_INVALIDATE选项。该选项有TRUE、FALSE和DBMS_STATS.AUTO_INVALIDATE这3个值。如果取值为TRUE,那么表示收集统计信息后不进行游标失效动作,原有的Shared Cursor保持原有状态。如果取值为FALSE,那么表示将统计信息对象相关的所有Cursor全部失效,目标SQL语句在下次执行时就会使用硬解析。如果设置为AUTO_INVALIDATE,那么Oracle自己决定Shared Cursor失效动作,当SQL再次执行时间距离上次收集统计信息的时间超过5小时(隐含参数“_OPTIMIZER_INVALIDATION_PERIOD”决定)则对SQL重新做硬解析。AUTO_INVALIDATE为默认选项。有些DBA在收集统计信息时,没有使用NO_INVALIDATE=>FALSE选项,所以,即使收集了统计信息,执行计划也不会立即改变。可以在表级别设置让所有依赖于该表的游标不失效,设置方法为:
1EXEC DBMS_STATS.SET_TABLE_PREFS('SH','SALES','NO_INVALIDATE','TRUE');--在收集SH.SALES表上的统计信息时,让所有依赖于该表的游标不失效--在收集SH.SALES表上的统计信息时,让所有依赖于该表的游标不失效
实验一:
1CREATE TABLE TEST_EXPLAIN_LHR AS SELECT * FROM DBA_OBJECTS; 2INSERT INTO TEST_EXPLAIN_LHR SELECT * FROM TEST_EXPLAIN_LHR; 3COMMIT; 4SELECT COUNT(*) FROM TEST_EXPLAIN_LHR; 5 6CREATE INDEX IDX_OBJ_LHR ON TEST_EXPLAIN_LHR(OBJECT_ID); 7EXEC DBMS_STATS.GATHER_TABLE_STATS(USER,'test_explain_lhr',ESTIMATE_PERCENT => 100,CASCADE => TRUE); 8 9VAR X NUMBER;10VAR Y NUMBER;11EXEC :X := 0;12EXEC :Y := 100000;1314EXPLAIN PLAN FOR SELECT COUNT(*) FROM TEST_EXPLAIN_LHR T WHERE T.OBJECT_ID BETWEEN :X AND :Y ;1516SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);171819SET AUTOT ON20SELECT COUNT(*) FROM TEST_EXPLAIN_LHR T WHERE T.OBJECT_ID BETWEEN :X AND :Y ;2122SET AUTOT OFF23SELECT COUNT(*) FROM TEST_EXPLAIN_LHR T WHERE T.OBJECT_ID BETWEEN :X AND :Y ;24SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(NULL,NULL,'advanced'));CREATE TABLE TEST_EXPLAIN_LHR AS SELECT * FROM DBA_OBJECTS;
2INSERT INTO TEST_EXPLAIN_LHR SELECT * FROM TEST_EXPLAIN_LHR;
3COMMIT;
4SELECT COUNT(*) FROM TEST_EXPLAIN_LHR;
5
6CREATE INDEX IDX_OBJ_LHR ON TEST_EXPLAIN_LHR(OBJECT_ID);
7EXEC DBMS_STATS.GATHER_TABLE_STATS(USER,'test_explain_lhr',ESTIMATE_PERCENT => 100,CASCADE => TRUE);
8
9VAR X NUMBER;
10VAR Y NUMBER;
11EXEC :X := 0;
12EXEC :Y := 100000;
13
14EXPLAIN PLAN FOR SELECT COUNT(*) FROM TEST_EXPLAIN_LHR T WHERE T.OBJECT_ID BETWEEN :X AND :Y ;
15
16SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);
17
18
19SET AUTOT ON
20SELECT COUNT(*) FROM TEST_EXPLAIN_LHR T WHERE T.OBJECT_ID BETWEEN :X AND :Y ;
21
22SET AUTOT OFF
23SELECT COUNT(*) FROM TEST_EXPLAIN_LHR T WHERE T.OBJECT_ID BETWEEN :X AND :Y ;
24SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(NULL,NULL,'advanced'));
下面实验验证了使用EXPLAIN PLAN FOR和SET AUTOT ON方式获取到的执行计划都是不准确的:
1SYS@PROD1> clear scr 2SYS@PROD1> CREATE TABLE test_explain_lhr AS SELECT * FROM Dba_Objects; 3 4Table created. 5 6SYS@PROD1> INSERT INTO test_explain_lhr SELECT * FROM test_explain_lhr; 7 872503 rows created. 9 10SYS@PROD1> COMMIT; 11 12Commit complete. 13 14SYS@PROD1> SELECT COUNT(*) FROM test_explain_lhr; 15 16 COUNT(*) 17---------- 18 145006 19 20SYS@PROD1> CREATE INDEX idx_obj_lhr ON test_explain_lhr(object_id); 21 22Index created. 23 24SYS@PROD1> EXEC dbms_stats.gather_table_stats(USER,'test_explain_lhr',estimate_percent => 100,cascade => TRUE); 25 26PL/SQL procedure successfully completed. 27 28SYS@PROD1> VAR x NUMBER; 29SYS@PROD1> VAR y NUMBER; 30SYS@PROD1> EXEC :x := 0; 31 32PL/SQL procedure successfully completed. 33 34SYS@PROD1> EXEC :y := 100000; 35 36PL/SQL procedure successfully completed. 37 38SYS@PROD1> EXPLAIN PLAN FOR SELECT COUNT(*) FROM test_explain_lhr t WHERE t.object_id BETWEEN :x AND :y ; 39 40Explained. 41 42 43SYS@PROD1> set line 9999 44SYS@PROD1> SELECT * FROM TABLE(dbms_xplan.display); 45 46PLAN_TABLE_OUTPUT 47--------------------------------------------------------------------------------------------- 48Plan hash value: 3299589416 49 50---------------------------------------------------------------------------------- 51| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | 52---------------------------------------------------------------------------------- 53| 0 | SELECT STATEMENT | | 1 | 5 | 3 (0)| 00:00:01 | 54| 1 | SORT AGGREGATE | | 1 | 5 | | | 55|* 2 | FILTER | | | | | | 56|* 3 | INDEX RANGE SCAN| IDX_OBJ_LHR | 363 | 1815 | 3 (0)| 00:00:01 | 57---------------------------------------------------------------------------------- 58 59Predicate Information (identified by operation id): 60--------------------------------------------------- 61 62 2 - filter(TO_NUMBER(:X)<=TO_NUMBER(:Y)) 63 3 - access("T"."OBJECT_ID">=TO_NUMBER(:X) AND 64 "T"."OBJECT_ID"<=TO_NUMBER(:Y)) 65 6617 rows selected. 67 68SYS@PROD1> set autot on 69SYS@PROD1> SELECT COUNT(*) FROM test_explain_lhr t WHERE t.object_id BETWEEN :x AND :y ; 70 71 COUNT(*) 72---------- 73 145006 74 75 76Execution Plan 77---------------------------------------------------------- 78Plan hash value: 3299589416 79 80---------------------------------------------------------------------------------- 81| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | 82---------------------------------------------------------------------------------- 83| 0 | SELECT STATEMENT | | 1 | 5 | 3 (0)| 00:00:01 | 84| 1 | SORT AGGREGATE | | 1 | 5 | | | 85|* 2 | FILTER | | | | | | 86|* 3 | INDEX RANGE SCAN| IDX_OBJ_LHR | 363 | 1815 | 3 (0)| 00:00:01 | 87---------------------------------------------------------------------------------- 88 89Predicate Information (identified by operation id): 90--------------------------------------------------- 91 92 2 - filter(TO_NUMBER(:X)<=TO_NUMBER(:Y)) 93 3 - access("T"."OBJECT_ID">=TO_NUMBER(:X) AND 94 "T"."OBJECT_ID"<=TO_NUMBER(:Y)) 95 96 97Statistics 98---------------------------------------------------------- 99 1 recursive calls100 0 db block gets101 329 consistent gets102 0 physical reads103 0 redo size104 424 bytes sent via SQL*Net to client105 419 bytes received via SQL*Net from client106 2 SQL*Net roundtrips to/from client107 0 sorts (memory)108 0 sorts (disk)109 1 rows processed110111SYS@PROD1> SET AUTOT OFF112SYS@PROD1> SELECT COUNT(*) FROM test_explain_lhr t WHERE t.object_id BETWEEN :x AND :y ;113114 COUNT(*)115----------116 145006117118SYS@PROD1> SELECT * FROM TABLE(dbms_xplan.display_cursor(NULL,NULL,'advanced'));119120PLAN_TABLE_OUTPUT121----------------------------------------------------------------------------------------122SQL_ID 1r87sg98rdkuf, child number 0123-------------------------------------124SELECT COUNT(*) FROM test_explain_lhr t WHERE t.object_id BETWEEN :x125AND :y126127Plan hash value: 2428225634128129--------------------------------------------------------------------------------------130| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |131--------------------------------------------------------------------------------------132| 0 | SELECT STATEMENT | | | | 90 (100)| |133| 1 | SORT AGGREGATE | | 1 | 5 | | |134|* 2 | FILTER | | | | | |135|* 3 | INDEX FAST FULL SCAN| IDX_OBJ_LHR | 145K| 708K| 90 (2)| 00:00:02 |136--------------------------------------------------------------------------------------137138Query Block Name / Object Alias (identified by operation id):139-------------------------------------------------------------140141 1 - SEL$1142 3 - SEL$1 / T@SEL$1143144Outline Data145-------------146147 /*+148 BEGIN_OUTLINE_DATA149 IGNORE_OPTIM_EMBEDDED_HINTS150 OPTIMIZER_FEATURES_ENABLE('11.2.0.1')151 DB_VERSION('11.2.0.1')152 ALL_ROWS153 OUTLINE_LEAF(@"SEL$1")154 INDEX_FFS(@"SEL$1" "T"@"SEL$1" ("TEST_EXPLAIN_LHR"."OBJECT_ID"))155 END_OUTLINE_DATA156 */157158Peeked Binds (identified by position):159--------------------------------------160161 1 - :X (NUMBER): 0162 2 - :Y (NUMBER): 100000163164Predicate Information (identified by operation id):165---------------------------------------------------166167 2 - filter(:X<=:Y)168 3 - filter(("T"."OBJECT_ID">=:X AND "T"."OBJECT_ID"<=:Y))169170Column Projection Information (identified by operation id):171-----------------------------------------------------------172173 1 - (#keys=0) COUNT(*)[22]17417517653 rows selected.
2SYS@PROD1> CREATE TABLE test_explain_lhr AS SELECT * FROM Dba_Objects;
3
4Table created.
5
6SYS@PROD1> INSERT INTO test_explain_lhr SELECT * FROM test_explain_lhr;
7
872503 rows created.
9
10SYS@PROD1> COMMIT;
11
12Commit complete.
13
14SYS@PROD1> SELECT COUNT(*) FROM test_explain_lhr;
15
16 COUNT(*)
17----------
18 145006
19
20SYS@PROD1> CREATE INDEX idx_obj_lhr ON test_explain_lhr(object_id);
21
22Index created.
23
24SYS@PROD1> EXEC dbms_stats.gather_table_stats(USER,'test_explain_lhr',estimate_percent => 100,cascade => TRUE);
25
26PL/SQL procedure successfully completed.
27
28SYS@PROD1> VAR x NUMBER;
29SYS@PROD1> VAR y NUMBER;
30SYS@PROD1> EXEC :x := 0;
31
32PL/SQL procedure successfully completed.
33
34SYS@PROD1> EXEC :y := 100000;
35
36PL/SQL procedure successfully completed.
37
38SYS@PROD1> EXPLAIN PLAN FOR SELECT COUNT(*) FROM test_explain_lhr t WHERE t.object_id BETWEEN :x AND :y ;
39
40Explained.
41
42
43SYS@PROD1> set line 9999
44SYS@PROD1> SELECT * FROM TABLE(dbms_xplan.display);
45
46PLAN_TABLE_OUTPUT
47---------------------------------------------------------------------------------------------
48Plan hash value: 3299589416
49
50----------------------------------------------------------------------------------
51| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
52----------------------------------------------------------------------------------
53| 0 | SELECT STATEMENT | | 1 | 5 | 3 (0)| 00:00:01 |
54| 1 | SORT AGGREGATE | | 1 | 5 | | |
55|* 2 | FILTER | | | | | |
56|* 3 | INDEX RANGE SCAN| IDX_OBJ_LHR | 363 | 1815 | 3 (0)| 00:00:01 |
57----------------------------------------------------------------------------------
58
59Predicate Information (identified by operation id):
60---------------------------------------------------
61
62 2 - filter(TO_NUMBER(:X)<=TO_NUMBER(:Y))
63 3 - access("T"."OBJECT_ID">=TO_NUMBER(:X) AND
64 "T"."OBJECT_ID"<=TO_NUMBER(:Y))
65
6617 rows selected.
67
68SYS@PROD1> set autot on
69SYS@PROD1> SELECT COUNT(*) FROM test_explain_lhr t WHERE t.object_id BETWEEN :x AND :y ;
70
71 COUNT(*)
72----------
73 145006
74
75
76Execution Plan
77----------------------------------------------------------
78Plan hash value: 3299589416
79
80----------------------------------------------------------------------------------
81| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
82----------------------------------------------------------------------------------
83| 0 | SELECT STATEMENT | | 1 | 5 | 3 (0)| 00:00:01 |
84| 1 | SORT AGGREGATE | | 1 | 5 | | |
85|* 2 | FILTER | | | | | |
86|* 3 | INDEX RANGE SCAN| IDX_OBJ_LHR | 363 | 1815 | 3 (0)| 00:00:01 |
87----------------------------------------------------------------------------------
88
89Predicate Information (identified by operation id):
90---------------------------------------------------
91
92 2 - filter(TO_NUMBER(:X)<=TO_NUMBER(:Y))
93 3 - access("T"."OBJECT_ID">=TO_NUMBER(:X) AND
94 "T"."OBJECT_ID"<=TO_NUMBER(:Y))
95
96
97Statistics
98----------------------------------------------------------
99 1 recursive calls
100 0 db block gets
101 329 consistent gets
102 0 physical reads
103 0 redo size
104 424 bytes sent via SQL*Net to client
105 419 bytes received via SQL*Net from client
106 2 SQL*Net roundtrips to/from client
107 0 sorts (memory)
108 0 sorts (disk)
109 1 rows processed
110
111SYS@PROD1> SET AUTOT OFF
112SYS@PROD1> SELECT COUNT(*) FROM test_explain_lhr t WHERE t.object_id BETWEEN :x AND :y ;
113
114 COUNT(*)
115----------
116 145006
117
118SYS@PROD1> SELECT * FROM TABLE(dbms_xplan.display_cursor(NULL,NULL,'advanced'));
119
120PLAN_TABLE_OUTPUT
121----------------------------------------------------------------------------------------
122SQL_ID 1r87sg98rdkuf, child number 0
123-------------------------------------
124SELECT COUNT(*) FROM test_explain_lhr t WHERE t.object_id BETWEEN :x
125AND :y
126
127Plan hash value: 2428225634
128
129--------------------------------------------------------------------------------------
130| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
131--------------------------------------------------------------------------------------
132| 0 | SELECT STATEMENT | | | | 90 (100)| |
133| 1 | SORT AGGREGATE | | 1 | 5 | | |
134|* 2 | FILTER | | | | | |
135|* 3 | INDEX FAST FULL SCAN| IDX_OBJ_LHR | 145K| 708K| 90 (2)| 00:00:02 |
136--------------------------------------------------------------------------------------
137
138Query Block Name / Object Alias (identified by operation id):
139-------------------------------------------------------------
140
141 1 - SEL$1
142 3 - SEL$1 / T@SEL$1
143
144Outline Data
145-------------
146
147 /*+
148 BEGIN_OUTLINE_DATA
149 IGNORE_OPTIM_EMBEDDED_HINTS
150 OPTIMIZER_FEATURES_ENABLE('11.2.0.1')
151 DB_VERSION('11.2.0.1')
152 ALL_ROWS
153 OUTLINE_LEAF(@"SEL$1")
154 INDEX_FFS(@"SEL$1" "T"@"SEL$1" ("TEST_EXPLAIN_LHR"."OBJECT_ID"))
155 END_OUTLINE_DATA
156 */
157
158Peeked Binds (identified by position):
159--------------------------------------
160
161 1 - :X (NUMBER): 0
162 2 - :Y (NUMBER): 100000
163
164Predicate Information (identified by operation id):
165---------------------------------------------------
166
167 2 - filter(:X<=:Y)
168 3 - filter(("T"."OBJECT_ID">=:X AND "T"."OBJECT_ID"<=:Y))
169
170Column Projection Information (identified by operation id):
171-----------------------------------------------------------
172
173 1 - (#keys=0) COUNT(*)[22]
174
175
17653 rows selected.
& 说明:
有关真实的执行计划的更多内容可以参考我的BLOG:http://blog.itpub.net/26736162/viewspace-2152884/
本文选自《Oracle程序员面试笔试宝典》,作者:李华荣。
---------------优质麦课------------
详细内容可以添加麦老师微信或QQ私聊。
About Me:小麦苗
● 本文作者:小麦苗,只专注于数据库的技术,更注重技术的运用
● 作者博客地址:http://blog.itpub.net/26736162/abstract/1/
● 本系列题目来源于作者的学习笔记,部分整理自网络,若有侵权或不当之处还请谅解
● 版权所有,欢迎分享本文,转载请保留出处
● QQ:646634621 QQ群:618766405
● 提供OCP、OCM和高可用部分最实用的技能培训
● 题目解答若有不当之处,还望各位朋友批评指正,共同进步
长按下图识别二维码或微信扫描下图二维码来关注小麦苗的微信公众号:xiaomaimiaolhr,学习最实用的数据库技术。
喜欢就点击“好看”吧