ITPub博客

首页 > Linux操作系统 > Linux操作系统 > 统计信息不准确而产生的merge join cartesian

统计信息不准确而产生的merge join cartesian

原创 Linux操作系统 作者:to_be_dba 时间:2013-03-05 19:27:38 0 删除 编辑

对三个表进行联合查询,其中ref_glbal和ref_al数据量都不大,有五百多条数据。TABLE1有75万数据。
将三个表进行内连接,产生了如下所示的执行计划。非常不正常。

SQL> explain plan for
  2  select count(1) from  SCHEMA1.TABLE1 inner join ref_glbal rg on  trim(TABLE1.ND_AIR) = rg.air_code
  3  inner join ref_al ra  on rg.glbal_seq = ra.glbal_seq
  4  ;

Explained

SQL> select * from table(dbms_xplan.display);

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 3439537040
--------------------------------------------------------------------------------
| Id  | Operation                     | Name                       | Rows  | Byt
--------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |                            |     1 |
|   1 |  SORT AGGREGATE               |                            |     1 |
|   2 |   NESTED LOOPS                |                            |       |
|   3 |    NESTED LOOPS               |                            |     1 |
|   4 |     MERGE JOIN CARTESIAN      |                            |     1 |
|   5 |      INDEX FULL SCAN          | IDX_ref_al_glbal |     1 |
|   6 |      BUFFER SORT              |                            |   757K|  44
|   7 |       INDEX FAST FULL SCAN    | TABLE1I2                    |   757K|  44
|*  8 |     INDEX UNIQUE SCAN         | PK_ref_glbal          |     1 |
|*  9 |    TABLE ACCESS BY INDEX ROWID| ref_glbal             |     1 |
--------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
   8 - access("RG"."glbal_SEQ"="RA"."glbal_SEQ")
   9 - filter("RG"."AIR_CODE"=TRIM("TABLE1"."ND_AIR"))

22 rows selected

 

ra、rg、TABLE1做内连接,并不缺少连接条件,为什么产生了“MERGE JOIN CARTESIAN”呢?


查看了数据字典,发现table1表nd_air列的统计信息不对。本来是三位的字段,统计的最大值和最小值居然都是10位左右。

dba_tab_columns部分列如下:
OWNER TABLE_NAME COLUMN_NAME DATA_TYPE NUM_DISTINCT LOW_VALUE HIGH_VALUE DENSITY LAST_ANALYZED SAMPLE_SIZE
SCHEMA1 TABLE1  ND_AIR CHAR 50  3030312020 3938382020 6.53115E-07 2012-7-8 7:53:40 6556
查看该表的最大值和最小值:
SQL> select max(nd_Air),min(nd_air) from SCHEMA1.TABLE1;

MAX(ND_AIR) MIN(ND_AIR)
----------- -----------
988         001


尝试重新收集统计信息,无权限:
SQL> exec dbms_stats.gather_table_stats(ownname => 'SCHEMA1',tabname => 'TABLE1',cascade => true);

begin dbms_stats.gather_table_stats(ownname => 'SCHEMA1',tabname => 'TABLE1',cascade => true); end;

ORA-20000: Unable to analyze TABLE "SCHEMA1"."TABLE1", insufficient privileges or does not exist
ORA-06512: 在 "SYS.DBMS_STATS", line 23818
ORA-06512: 在 "SYS.DBMS_STATS", line 23880
ORA-06512: 在 line 1

以上是第一天的记录,由于没有仔细看执行计划,只看到merge join castesian就觉得有问题,而且非常自然地想到是最大的表table1的问题,研究不够仔细。
今天在组长的提示下,看到了工作中的不仔细。
上面提到过,ref_glbal和ref_al数据量都有五百多条数据,但在最初的执行计划中却都认为只有一条数据。由于两个表中的数据都是新导入的,因此出现了问题。
再来说merge join castesian,由于对笛卡尔积的偏见,直接认为这个执行计划不对。察看了一下网上的文档[http://blog.csdn.net/heqiyu34/article/details/7543778]

--------------------------------------------------------------------------------
Plan hash value: 3439537040
--------------------------------------------------------------------------------
| Id  | Operation                     | Name                       | Rows  | Byt
--------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |                            |     1 |
|   1 |  SORT AGGREGATE               |                            |     1 |
|   2 |   NESTED LOOPS                |                            |       |
|   3 |    NESTED LOOPS               |                            |     1 |
|   4 |     MERGE JOIN CARTESIAN      |                            |     1 |
|   5 |      INDEX FULL SCAN          | IDX_ref_al_glbal |     1 |
|   6 |      BUFFER SORT              |                            |   757K|  44
|   7 |       INDEX FAST FULL SCAN    | TABLE1I2                    |   757K|  44
|*  8 |     INDEX UNIQUE SCAN         | PK_ref_glbal          |     1 |
|*  9 |    TABLE ACCESS BY INDEX ROWID| ref_glbal             |     1 |
--------------------------------------------------------------------------------

通过问题的复现,确认了以上执行计划就是由于ref_glbal和ref_al的统计信息不准确造成的。
但是网上大部分关于sort join cartesian的解释都是说两个非常小的表进行此连接比较合适。
而在我的例子中,是一个大表和一个小表连接。

ref_glbal作为一个中间连接被最后执行了,这个问题最匪夷所思的地方是:为什么一个757K数据量的表,oracle会认为带有排序操作更加高效呢?

 

 

 

SQL> explain plan for select /*+use_nl(ra rg)*/*
  2     from ref_al ra
  3      inner join ref_glbal rg on rg.glbal_seq = ra.glbal_seq;

已解释。

SQL> select * from table(dbms_xplan.display);

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 1473932376
--------------------------------------------------------------------------------------------------
| Id  | Operation                    | Name              | Rows  | Bytes | Cost(%CPU)| Time     |
--------------------------------------------------------------------------------------------------
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |                   |   555 |   138K|   574   (1)| 00:00:07 |
|   1 |  NESTED LOOPS                |                   |       |       |      |          |
|   2 |   NESTED LOOPS               |                   |   555 |   138K|   574   (1)| 00:00:07 |
|   3 |    TABLE ACCESS FULL         | REF_al       |   555 | 77145 |    18   (0)| 00:00:01 |
|*  4 |    INDEX UNIQUE SCAN         | PK_REF_GLBal |     1 |       |     0   (0)| 00:00:01 |
|   5 |   TABLE ACCESS BY INDEX ROWID| REF_GLBal    |     1 |   117 |     1   (0)| 00:00:01 |
--------------------------------------------------------------------------------------------------

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------

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

   4 - access("RG"."GLBal_SEQ"="RA"."GLBal_SEQ")

已选择17行。

 


SQL> explain plan for select *
  2     from ref_al ra
  3      inner join ref_glbal rg on rg.glbal_seq = ra.glbal_seq;

已解释。

SQL> select * from table(dbms_xplan.display);

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 360641786
--------------------------------------------------------------------------------------------------
| Id  | Operation                    | Name              | Rows  | Bytes | Cost(%CPU)| Time     |
--------------------------------------------------------------------------------------------------
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |                   |   555 |   138K|    32   (4)| 00:00:01 |
|   1 |  MERGE JOIN                  |                   |   555 |   138K|    32   (4)| 00:00:01 |
|   2 |   TABLE ACCESS BY INDEX ROWID| REF_GLBal    |   569 | 66573 |    13   (0)| 00:00:01 |
|   3 |    INDEX FULL SCAN           | PK_REF_GLBal |   569 |       |     3   (0)| 00:00:01 |
|*  4 |   SORT JOIN                  |                   |   555 | 77145 |    19   (6)| 00:00:01 |
|   5 |    TABLE ACCESS FULL         | REF_al       |   555 | 77145 |    18   (0)| 00:00:01 |
--------------------------------------------------------------------------------------------------

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------

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

   4 - access("RG"."GLBal_SEQ"="RA"."GLBal_SEQ")
       filter("RG"."GLBal_SEQ"="RA"."GLBal_SEQ")

已选择18行。


===================================
错误的执行计划:

SQL> explain plan for select count(1)
  2     from ref_al ra
  3      inner join ref_glbal rg on rg.glbal_seq = ra.glbal_seq
  4     inner join SCHEMA1.TABLE1 on  trim(TABLE1.ND_AIR) = rg.air_code;

已解释。

SQL> select * from table(dbms_xplan.display);

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 3439537040
------------------------------------------------------------------------------------------------------------
| Id  | Operation                     | Name                       | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------------------------
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |                            |     1 |36 |   929   (2)| 00:00:12 |
|   1 |  SORT AGGREGATE               |                            |     1 |36 |            |          |
|   2 |   NESTED LOOPS                |                            |       |   |            |          |
|   3 |    NESTED LOOPS               |                            |     1 |36 |   929   (2)| 00:00:12 |
|   4 |     MERGE JOIN CARTESIAN      |                            |     1 |19 |   929   (2)| 00:00:12 |
|   5 |      INDEX FULL SCAN          | IDX_REF_al_GLBal |     1 |13 |     1   (0)| 00:00:01 |
|   6 |      BUFFER SORT              |                            |   757K|  4437K|   928   (2)| 00:00:12 |
|   7 |       INDEX FAST FULL SCAN    | TABLE1I2                    |   757K|  4437K|   928   (2)| 00:00:12 |
|*  8 |     INDEX UNIQUE SCAN         | PK_REF_GLBal          |     1 |   |     0   (0)| 00:00:01 |
|*  9 |    TABLE ACCESS BY INDEX ROWID| REF_GLBal             |     1 |17 |     0   (0)| 00:00:01 |
------------------------------------------------------------------------------------------------------------

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------

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

   8 - access("RG"."GLBal_SEQ"="RA"."GLBal_SEQ")
   9 - filter("RG"."AIR_CODE"=TRIM("TABLE1"."ND_AIR"))

已选择22行。

 

=================================
使用hint禁用了merge join castrsian
SQL> explain plan for select /*+ OPT_PARAM('_optimizer_mjc_enabled','false')*/count(1)
  2     from ref_al ra
  3      inner join ref_glbal rg on rg.glbal_seq = ra.glbal_seq
  4     inner join SCHEMA1.TABLE1 on  trim(TABLE1.ND_AIR) = rg.air_code;

已解释。

SQL> select * from table(dbms_xplan.display);

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 13807094
------------------------------------------------------------------------------------------------------------
| Id  | Operation                     | Name                       | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------------------------

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |                            |     1 |36 |   929   (2)| 00:00:12 |
|   1 |  SORT AGGREGATE               |                            |     1 |36 |            |          |
|   2 |   NESTED LOOPS                |                            |       |   |            |          |
|   3 |    NESTED LOOPS               |                            |     1 |36 |   929   (2)| 00:00:12 |
|   4 |     NESTED LOOPS              |                            |     1 |19 |   929   (2)| 00:00:12 |
|   5 |      INDEX FULL SCAN          | IDX_REF_al_GLBal |     1 |13 |     1   (0)| 00:00:01 |
|   6 |      INDEX FAST FULL SCAN     | TABLE1I2                    |   757K|  4437K|   928   (2)| 00:00:12 |
|*  7 |     INDEX UNIQUE SCAN         | PK_REF_GLBal          |     1 |   |     0   (0)| 00:00:01 |
|*  8 |    TABLE ACCESS BY INDEX ROWID| REF_GLBal             |     1 |17 |     0   (0)| 00:00:01 |
------------------------------------------------------------------------------------------------------------

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

   7 - access("RG"."GLBal_SEQ"="RA"."GLBal_SEQ")
   8 - filter("RG"."AIR_CODE"=TRIM("TABLE1"."ND_AIR"))

已选择21行。

来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/26451536/viewspace-755442/ ,如需转载,请注明出处,否则将追究法律责任。

上一篇: DBMS_SQL
下一篇: truncate 命令使用
请登录后发表评论 登录
全部评论

注册时间:2011-11-23

  • 博文量
    170
  • 访问量
    484896