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/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/7490392/viewspace-1041223/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值