oracle的執行計劃居然出錯[轉帖]
今天同事發給我一個sql,說查詢不到結果,sql本身沒有錯誤。而且在其他伺服器上執行可以得到結果。
環境如下:
SQL> select * from v$version;
[@more@]
BANNER
----------------------------------------------------------------
Oracle9i Enterprise Edition Release 9.2.0.4.0 - 64bit Production
PL/SQL Release 9.2.0.4.0 - Production
CORE 9.2.0.3.0 Production
TNS for Solaris: Version 9.2.0.4.0 - Production
NLSRTL Version 9.2.0.4.0 - Production
表結構:
SQL> desc plt_plat
Name Null? Type
-------------------------------------- -------- ----------------
ID NOT NULL CHAR(24)
PLAT_FATHER CHAR(24)
PLAT_CLASS CHAR(1)
PLAT_GRADE NUMBER(2)
PLAT_NAME NOT NULL VARCHAR2(30)
PLAT_LEVEL VARCHAR2(10)
PLAT_DESC VARCHAR2(500)
PLAT_ROOT CHAR(24)
PLAT_CODE VARCHAR2(30)
查詢語句:
select pp.id, pp.plat_name, pp.plat_class, city.id, city.plat_name, city.plat_class
,province.id,province.plat_name,province.plat_class
from plt_plat pp,
plt_plat city,
plt_plat province
where pp.plat_father=city.id
and city.plat_father=province.id
and pp.plat_class=4
由於這張表是物化檢視複製生成的,首先我檢查了物件的狀態,然後檢查了物化檢視的指令碼,並重新重新整理了這張物化檢視。錯誤依舊。和其他伺服器上的表進行表結構的對比,沒有發現錯誤。檢查表中的資料,發現和其他伺服器上的完全一致。使用語句analyze table plt_plat validate structure cascade檢查表和索引結構,未發現異常。
懷疑是否是錯誤的統計資訊造成的,執行語句analyze table plt_plat delete statistics;,結果發現得到了正確的結果。
然後重新收集統計資訊analyze table plt_plat compute statistics,錯誤又出現了。
懷疑和執行路徑有關:
SQL> select pp.id, pp.plat_name, pp.plat_class, city.id, city.plat_name, city.plat_class
2 ,province.id,province.plat_name,province.plat_class
3 from plt_plat pp,
4 plt_plat city,
5 plt_plat province
6 where pp.plat_father=city.id
7 and city.plat_father=province.id
8 and pp.plat_class=4
9 ;
no rows selected
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=7 Card=1 Bytes=132)
1 0 HASH JOIN (Cost=7 Card=1 Bytes=132)
2 1 TABLE ACCESS (FULL) OF 'PLT_PLAT' (Cost=3 Card=1 Bytes=72)
3 1 TABLE ACCESS (FULL) OF 'PLT_PLAT' (Cost=3 Card=330 Bytes=19800)
感覺很奇怪,怎麼三張表的連線,執行計劃裡面只有兩張表關聯。
SQL> select pp.id, pp.plat_name, pp.plat_class, city.id, city.plat_name, city.plat_class
2 ,province.id,province.plat_name,province.plat_class
3 from plt_plat pp,
4 plt_plat city,
5 plt_plat province
6 where pp.plat_father=city.id
7 and city.plat_father=province.id
8 and pp.plat_class=4
9 ;
149 rows selected.
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=RULE
1 0 NESTED LOOPS
2 1 NESTED LOOPS
3 2 TABLE ACCESS (FULL) OF 'PLT_PLAT'
4 2 TABLE ACCESS (BY INDEX ROWID) OF 'PLT_PLAT'
5 4 INDEX (UNIQUE SCAN) OF 'PK_PLT_PLAT' (UNIQUE)
6 1 TABLE ACCESS (BY INDEX ROWID) OF 'PLT_PLAT'
7 6 INDEX (UNIQUE SCAN) OF 'PK_PLT_PLAT' (UNIQUE)
採用rule模式,執行計劃是正確的。
下面是在其他伺服器上相同表(也是透過物化檢視複製生成的)上執行查詢的結果。
SQL> select pp.id, pp.plat_name, pp.plat_class, city.id, city.plat_name, city.plat_class
2 ,province.id,province.plat_name,province.plat_class
3 from plt_plat pp,
4 plt_plat city,
5 plt_plat province
6 where pp.plat_father=city.id
7 and city.plat_father=province.id
8 and pp.plat_class=4
9 ;
149 rows selected.
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=8 Card=76 Bytes=12692)
1 0 HASH JOIN (Cost=8 Card=76 Bytes=12692)
2 1 HASH JOIN (Cost=5 Card=76 Bytes=9728)
3 2 TABLE ACCESS (FULL) OF 'PLT_PLAT' (Cost=2 Card=76 Bytes=4864)
4 2 TABLE ACCESS (FULL) OF 'PLT_PLAT' (Cost=2 Card=304 Bytes=19456)
5 1 TABLE ACCESS (FULL) OF 'PLT_PLAT' (Cost=2 Card=304 Bytes=11856)
錯誤已經很明顯了,oracle的執行計劃出錯。
是什麼原因造成的執行計劃出錯呢?仔細檢查初始化引數後發現,query_rewrite_enabled的值是TRUE。
一直沒有注意這個引數,是因為的建立物化檢視複製的時候並沒有指定ENABLE QUERY REWRITE子句,在PLT_PLAT表上也沒有建立其它物化檢視,而且初始化引數query_rewrite_integrity的值是enforced,這是最嚴格的設定,沒有想到在這種情況下oracle仍然使用了query rewrite,而且查詢重寫後得到的結果也不正確。
這樣看來oracle的查詢重寫、統計資訊的收集以及CBO執行策略還是有些問題的。
下面簡要描述一下不同情況下會出現什麼問題。
1.將查詢重寫關閉,無論是否有統計資訊,也無論統計資訊生成的方式,查詢都不會出錯。
SQL> alter session set query_rewrite_enabled = false;
Session altered.
SQL> set autot on exp
SQL> select pp.id, pp.plat_name, pp.plat_class, city.id, city.plat_name, city.plat_class
2 ,province.id,province.plat_name,province.plat_class
3 from plt_plat pp,
4 plt_plat city,
5 plt_plat province
6 where pp.plat_father=city.id
7 and city.plat_father=province.id
8 and pp.plat_class=4
9 ;
149 rows selected.
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=11 Card=84 Bytes=14028)
1 0 HASH JOIN (Cost=11 Card=84 Bytes=14028)
2 1 HASH JOIN (Cost=7 Card=84 Bytes=10752)
3 2 TABLE ACCESS (FULL) OF 'PLT_PLAT' (Cost=3 Card=84 Bytes=5376)
4 2 TABLE ACCESS (FULL) OF 'PLT_PLAT' (Cost=3 Card=334 Bytes=21376)
5 1 TABLE ACCESS (FULL) OF 'PLT_PLAT' (Cost=3 Card=334 Bytes=13026)
SQL> analyze table plt_plat delete statistics;
Table analyzed.
SQL> select pp.id, pp.plat_name, pp.plat_class, city.id, city.plat_name, city.plat_class
2 ,province.id,province.plat_name,province.plat_class
3 from plt_plat pp,
4 plt_plat city,
5 plt_plat province
6 where pp.plat_father=city.id
7 and city.plat_father=province.id
8 and pp.plat_class=4
9 ;
149 rows selected.
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'PLT_PLAT'
2 1 NESTED LOOPS
3 2 NESTED LOOPS
4 3 TABLE ACCESS (FULL) OF 'PLT_PLAT'
5 3 TABLE ACCESS (BY INDEX ROWID) OF 'PLT_PLAT'
6 5 INDEX (RANGE SCAN) OF 'IND_PLT_PLAT_PLAT_FATHER' (NON-UNIQUE)
7 2 INDEX (RANGE SCAN) OF 'IND_PLT_PLAT_PLAT_FATHER' (NON-UNIQUE)
2.開啟查詢重寫,基於rule的查詢結果正確,如果沒有統計資訊,all_rows和first_rows結果都是錯誤的。
SQL> alter session set query_rewrite_enabled = true;
Session altered.
SQL> analyze table plt_plat delete statistics;
Table analyzed.
SQL> alter session set optimizer_mode = rule;
Session altered.
SQL> select pp.id, pp.plat_name, pp.plat_class, city.id, city.plat_name, city.plat_class
2 ,province.id,province.plat_name,province.plat_class
3 from plt_plat pp,
4 plt_plat city,
5 plt_plat province
6 where pp.plat_father=city.id
7 and city.plat_father=province.id
8 and pp.plat_class=4
9 ;
149 rows selected.
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=RULE
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'PLT_PLAT'
2 1 NESTED LOOPS
3 2 NESTED LOOPS
4 3 TABLE ACCESS (FULL) OF 'PLT_PLAT'
5 3 TABLE ACCESS (BY INDEX ROWID) OF 'PLT_PLAT'
6 5 INDEX (RANGE SCAN) OF 'IND_PLT_PLAT_PLAT_FATHER' (NON-UNIQUE)
7 2 INDEX (RANGE SCAN) OF 'IND_PLT_PLAT_PLAT_FATHER' (NON-UNIQUE)
SQL> alter session set optimizer_mode = first_rows;
Session altered.
SQL> select pp.id, pp.plat_name, pp.plat_class, city.id, city.plat_name, city.plat_class
2 ,province.id,province.plat_name,province.plat_class
3 from plt_plat pp,
4 plt_plat city,
5 plt_plat province
6 where pp.plat_father=city.id
7 and city.plat_father=province.id
8 and pp.plat_class=4
9 ;
no rows selected
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=FIRST_ROWS (Cost=29 Card=13 Bytes=2132)
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'PLT_PLAT' (Cost=2 Card=1 Bytes=72)
2 1 NESTED LOOPS (Cost=29 Card=13 Bytes=2132)
3 2 TABLE ACCESS (FULL) OF 'PLT_PLAT' (Cost=3 Card=13 Bytes=1196)
4 2 INDEX (RANGE SCAN) OF 'IND_PLT_PLAT_PLAT_FATHER' (NON-UNIQUE) (Cost=1 Card=1)
SQL> alter session set optimizer_mode = all_rows;
Session altered.
SQL> select pp.id, pp.plat_name, pp.plat_class, city.id, city.plat_name, city.plat_class
2 ,province.id,province.plat_name,province.plat_class
3 from plt_plat pp,
4 plt_plat city,
5 plt_plat province
6 where pp.plat_father=city.id
7 and city.plat_father=province.id
8 and pp.plat_class=4
9 ;
no rows selected
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=ALL_ROWS (Cost=7 Card=13 Bytes=2132)
1 0 HASH JOIN (Cost=7 Card=13 Bytes=2132)
2 1 TABLE ACCESS (FULL) OF 'PLT_PLAT' (Cost=3 Card=13 Bytes=1196)
3 1 TABLE ACCESS (FULL) OF 'PLT_PLAT' (Cost=3 Card=1307 Bytes=94104)
3.開啟查詢重寫,只有使用帶FOR ALL (INDEXED) COLUMNS子句的ANALYZE TABLE去收集統計資訊,CBO的結果才是正確的,使用其他方式收集統計資訊,CBO的查詢結果都是錯誤的。
SQL> analyze table plt_plat delete statistics;
Table analyzed.
SQL> alter session set optimizer_mode = choose;
Session altered.
SQL> analyze table plt_plat compute statistics;
Table analyzed.
SQL> select pp.id, pp.plat_name, pp.plat_class, city.id, city.plat_name, city.plat_class
2 ,province.id,province.plat_name,province.plat_class
3 from plt_plat pp,
4 plt_plat city,
5 plt_plat province
6 where pp.plat_father=city.id
7 and city.plat_father=province.id
8 and pp.plat_class=4
9 ;
no rows selected
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=7 Card=155 Bytes=20460)
1 0 HASH JOIN (Cost=7 Card=155 Bytes=20460)
2 1 TABLE ACCESS (FULL) OF 'PLT_PLAT' (Cost=3 Card=84 Bytes=6048)
3 1 TABLE ACCESS (FULL) OF 'PLT_PLAT' (Cost=3 Card=334 Bytes=20040)
SQL> analyze table plt_plat delete statistics;
Table analyzed.
SQL> exec dbms_stats.gather_table_stats(user, 'PLT_PLAT', method_opt => 'FOR ALL COLUMNS');
PL/SQL procedure successfully completed.
SQL> select pp.id, pp.plat_name, pp.plat_class, city.id, city.plat_name, city.plat_class
2 ,province.id,province.plat_name,province.plat_class
3 from plt_plat pp,
4 plt_plat city,
5 plt_plat province
6 where pp.plat_father=city.id
7 and city.plat_father=province.id
8 and pp.plat_class=4
9 ;
no rows selected
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=7 Card=84 Bytes=11928)
1 0 HASH JOIN (Cost=7 Card=84 Bytes=11928)
2 1 TABLE ACCESS (FULL) OF 'PLT_PLAT' (Cost=3 Card=151 Bytes=11778)
3 1 TABLE ACCESS (FULL) OF 'PLT_PLAT' (Cost=3 Card=334 Bytes=21376)
SQL> exec dbms_stats.delete_table_stats(user, 'PLT_PLAT');
PL/SQL procedure successfully completed.
SQL> analyze table plt_plat compute statistics for all columns;
Table analyzed.
SQL> select pp.id, pp.plat_name, pp.plat_class, city.id, city.plat_name, city.plat_class
2 ,province.id,province.plat_name,province.plat_class
3 from plt_plat pp,
4 plt_plat city,
5 plt_plat province
6 where pp.plat_father=city.id
7 and city.plat_father=province.id
8 and pp.plat_class=4
9 ;
149 rows selected.
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'PLT_PLAT'
2 1 NESTED LOOPS
3 2 NESTED LOOPS
4 3 TABLE ACCESS (FULL) OF 'PLT_PLAT'
5 3 TABLE ACCESS (BY INDEX ROWID) OF 'PLT_PLAT'
6 5 INDEX (RANGE SCAN) OF 'IND_PLT_PLAT_PLAT_FATHER' (NON-UNIQUE)
7 2 INDEX (RANGE SCAN) OF 'IND_PLT_PLAT_PLAT_FATHER' (NON-UNIQUE)
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/7490392/viewspace-1041223/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- oracle 10g執行計劃 (轉帖)Oracle 10g
- 檢視oracle執行計劃 - 轉Oracle
- ORACLE中檢視執行計劃(轉)Oracle
- 怎樣看懂Oracle的執行計劃[轉]Oracle
- Oracle9i 執行計劃(轉)Oracle
- ORACLE執行計劃Oracle
- 分析執行計劃優化SQLORACLE的執行計劃(轉)優化SQLOracle
- 【執行計劃】Oracle獲取執行計劃的幾種方法Oracle
- 【Oracle】-【索引-HINT,執行計劃】-帶HINT的索引執行計劃Oracle索引
- 分析執行計劃最佳化SQLORACLE的執行計劃(轉)SQLOracle
- ORACLE柱狀圖與執行計劃(轉)Oracle
- oracle 固定執行計劃Oracle
- Oracle sql執行計劃OracleSQL
- oracle sqlprofile 固定執行計劃,並遷移執行計劃OracleSQL
- 看懂Oracle中的執行計劃Oracle
- ORACLE執行計劃的介紹Oracle
- ORACLE執行計劃的檢視Oracle
- oracle執行計劃的使用(EXPLAIN)OracleAI
- Oracle中檢視已執行sql的執行計劃OracleSQL
- Oracle資料庫關於SQL的執行計劃(轉)Oracle資料庫SQL
- Oracle執行計劃詳解Oracle
- oracle固定執行計劃--sqlprofileOracleSQL
- Oracle 索引和執行計劃Oracle索引
- Oracle閱讀執行計劃Oracle
- oracle執行計劃相關Oracle
- oracle 執行計劃變更Oracle
- 【優化】Oracle 執行計劃優化Oracle
- oracle 執行計劃設定Oracle
- 轉:Oracle中檢視已執行sql的執行計劃---dbms_xplan.display_cursorOracleSQL
- Oracle檢視執行計劃的命令Oracle
- Oracle訪問表的執行計劃Oracle
- Oracle獲取執行計劃的方法Oracle
- oracle檢視執行計劃的方法Oracle
- 怎樣看懂Oracle的執行計劃Oracle
- 解析Oracle執行計劃的結果Oracle
- Oracle 檢視SQL的執行計劃OracleSQL
- oracle執行計劃------未走索引,隱式轉換的坑Oracle索引
- Oracle-繫結執行計劃Oracle