使用to_number,to_char转换的连接列易造成错误的执行计划

场景:偶尔会遇到一些表的列由于历史原因,本来都是数据的却是varchar2类型,结果只能通过建立to_char函数来进行连接,其结果易造成执行计划的不稳定以及错误的产生,
写SQL以及表设计时需注意;

SQL> select * from v$version;

BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production
PL/SQL Release 11.2.0.3.0 - Production
CORE    11.2.0.3.0      Production
TNS for Linux: Version 11.2.0.3.0 - Production
NLSRTL Version 11.2.0.3.0 - Production

SQL> create table t1 as select * from dba_objects  where 1=2;

Table created.

SQL> create table t2 as select * from dba_objects where 1=2;

Table created.

SQL> create table t3 as select * from dba_objects where 1=2;

Table created.

SQL> alter table t3 modify(object_id varchar2(40));

Table altered.

SQL> insert into t1 select * from dba_objects;

75253 rows created.

SQL> l
  1* insert into t1 select * from dba_objects
SQL> c/t1/t2
  1* insert into t2 select * from dba_objects
SQL> /

75253 rows created.

SQL> c/t2/t3
  1* insert into t3 select * from dba_objects
SQL> /

75253 rows created.

SQL> commit;

SQL> desc t1
 Name                                      Null?    Type
 ----------------------------------------- -------- ----------------------------
 OWNER                                              VARCHAR2(30)
 OBJECT_NAME                                        VARCHAR2(128)
 SUBOBJECT_NAME                                     VARCHAR2(30)
 OBJECT_ID                                          NUMBER
 DATA_OBJECT_ID                                     NUMBER
 OBJECT_TYPE                                        VARCHAR2(19)
 CREATED                                            DATE
 LAST_DDL_TIME                                      DATE
 TIMESTAMP                                          VARCHAR2(19)
 STATUS                                             VARCHAR2(7)
 TEMPORARY                                          VARCHAR2(1)
 GENERATED                                          VARCHAR2(1)
 SECONDARY                                          VARCHAR2(1)
 NAMESPACE                                          NUMBER
 EDITION_NAME                                       VARCHAR2(30)

SQL> desc t2
 Name                                      Null?    Type
 ----------------------------------------- -------- ----------------------------
 OWNER                                              VARCHAR2(30)
 OBJECT_NAME                                        VARCHAR2(128)
 SUBOBJECT_NAME                                     VARCHAR2(30)
 OBJECT_ID                                          NUMBER
 DATA_OBJECT_ID                                     NUMBER
 OBJECT_TYPE                                        VARCHAR2(19)
 CREATED                                            DATE
 LAST_DDL_TIME                                      DATE
 TIMESTAMP                                          VARCHAR2(19)
 STATUS                                             VARCHAR2(7)
 TEMPORARY                                          VARCHAR2(1)
 GENERATED                                          VARCHAR2(1)
 SECONDARY                                          VARCHAR2(1)
 NAMESPACE                                          NUMBER
 EDITION_NAME                                       VARCHAR2(30)

SQL> desc t3
 Name                                      Null?    Type
 ----------------------------------------- -------- ----------------------------
 OWNER                                              VARCHAR2(30)
 OBJECT_NAME                                        VARCHAR2(128)
 SUBOBJECT_NAME                                     VARCHAR2(30)
 OBJECT_ID                                          VARCHAR2(40)               --object_id类型设为varchar2
 DATA_OBJECT_ID                                     NUMBER
 OBJECT_TYPE                                        VARCHAR2(19)
 CREATED                                            DATE
 LAST_DDL_TIME                                      DATE
 TIMESTAMP                                          VARCHAR2(19)
 STATUS                                             VARCHAR2(7)
 TEMPORARY                                          VARCHAR2(1)
 GENERATED                                          VARCHAR2(1)
 SECONDARY                                          VARCHAR2(1)
 NAMESPACE                                          NUMBER
 EDITION_NAME                                       VARCHAR2(30)

SQL> create index idx_t1_object_id on t1(object_id);

Index created.

SQL> create index idx_t2_object_id on t2(object_id);

Index created.

SQL> create index idx_t3_object_id on t3(to_number(object_id));  --t3表列object_id为函数索引

Index created.

SQL> CREATE TABLE T4 AS SELECT * FROM DBA_OBJECTS;

Table created.

SQL> CREATE INDEX IDX_T4_OBJECT_ID ON T4(OBJECT_ID);

Index created.

exec dbms_stats.gather_table_stats('AIKI','T1',CASCADE=>TRUE,METHOD_OPT=>'FOR ALL INDEXED COLUMNS SIZE 1',estimate_percent=>100);
exec dbms_stats.gather_table_stats('AIKI','T2',CASCADE=>TRUE,METHOD_OPT=>'FOR ALL INDEXED COLUMNS SIZE 1',estimate_percent=>100);
exec dbms_stats.gather_table_stats('AIKI','T3',CASCADE=>TRUE,METHOD_OPT=>'FOR ALL INDEXED COLUMNS SIZE 1',estimate_percent=>100);
exec dbms_stats.gather_table_stats('AIKI','T4',CASCADE=>TRUE,METHOD_OPT=>'FOR ALL INDEXED COLUMNS SIZE 1',estimate_percent=>100);

--没有使用转换函数列的正确执行计划
SQL_ID  279zrj0d22082, child number 0
-------------------------------------
select t1.object_id, t1.object_name, T4.status, t2.owner from t1,T4,t2
where t1.object_id=T4.object_id and t2.object_id=T4.object_id and (
t1.object_id in (108,1080,10800) or t2.object_id in(108,1080,10800))

Plan hash value: 1275242484

---------------------------------------------------------------------------------------------------------------
| Id  | Operation                        | Name             | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
---------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                 |                  |      1 |        |      3 |00:00:00.01 |      48 |
|   1 |  CONCATENATION                   |                  |      1 |        |      3 |00:00:00.01 |      48 |
|   2 |   NESTED LOOPS                   |                  |      1 |        |      3 |00:00:00.01 |      27 |
|   3 |    NESTED LOOPS                  |                  |      1 |      3 |      3 |00:00:00.01 |      24 |
|   4 |     NESTED LOOPS                 |                  |      1 |      3 |      3 |00:00:00.01 |      18 |
|   5 |      INLIST ITERATOR             |                  |      1 |        |      3 |00:00:00.01 |       9 |
|   6 |       TABLE ACCESS BY INDEX ROWID| T2               |      3 |      3 |      3 |00:00:00.01 |       9 |
|*  7 |        INDEX RANGE SCAN          | IDX_T2_OBJECT_ID |      3 |      3 |      3 |00:00:00.01 |       6 |
|   8 |      TABLE ACCESS BY INDEX ROWID | T4               |      3 |      1 |      3 |00:00:00.01 |       9 |
|*  9 |       INDEX RANGE SCAN           | IDX_T4_OBJECT_ID |      3 |      1 |      3 |00:00:00.01 |       6 |
|* 10 |     INDEX RANGE SCAN             | IDX_T1_OBJECT_ID |      3 |      1 |      3 |00:00:00.01 |       6 |
|  11 |    TABLE ACCESS BY INDEX ROWID   | T1               |      3 |      1 |      3 |00:00:00.01 |       3 |
|  12 |   NESTED LOOPS                   |                  |      1 |        |      0 |00:00:00.01 |      21 |
|  13 |    NESTED LOOPS                  |                  |      1 |      3 |      0 |00:00:00.01 |      21 |
|  14 |     NESTED LOOPS                 |                  |      1 |      3 |      3 |00:00:00.01 |      16 |
|  15 |      INLIST ITERATOR             |                  |      1 |        |      3 |00:00:00.01 |       8 |
|  16 |       TABLE ACCESS BY INDEX ROWID| T1               |      3 |      3 |      3 |00:00:00.01 |       8 |
|* 17 |        INDEX RANGE SCAN          | IDX_T1_OBJECT_ID |      3 |      3 |      3 |00:00:00.01 |       5 |
|  18 |      TABLE ACCESS BY INDEX ROWID | T4               |      3 |      1 |      3 |00:00:00.01 |       8 |
|* 19 |       INDEX RANGE SCAN           | IDX_T4_OBJECT_ID |      3 |      1 |      3 |00:00:00.01 |       5 |
|* 20 |     INDEX RANGE SCAN             | IDX_T2_OBJECT_ID |      3 |      1 |      0 |00:00:00.01 |       5 |
|  21 |    TABLE ACCESS BY INDEX ROWID   | T2               |      0 |      1 |      0 |00:00:00.01 |       0 |
---------------------------------------------------------------------------------------------------------------

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

   7 - access(("T2"."OBJECT_ID"=108 OR "T2"."OBJECT_ID"=1080 OR "T2"."OBJECT_ID"=10800))
   9 - access("T2"."OBJECT_ID"="T4"."OBJECT_ID")
  10 - access("T1"."OBJECT_ID"="T4"."OBJECT_ID")
  17 - access(("T1"."OBJECT_ID"=108 OR "T1"."OBJECT_ID"=1080 OR "T1"."OBJECT_ID"=10800))
  19 - access("T1"."OBJECT_ID"="T4"."OBJECT_ID")
  20 - access("T2"."OBJECT_ID"="T4"."OBJECT_ID")
       filter((LNNVL("T2"."OBJECT_ID"=108) AND LNNVL("T2"."OBJECT_ID"=1080) AND
              LNNVL("T2"."OBJECT_ID"=10800)))


47 rows selected.

--使用转换函数to_number造成的错误执行计划
SQL> 
SQL> 
SQL> select  
  2  t1.object_id,
  3  t1.object_name,
  4  t3.status,
  5  t2.owner
  6  from t1,t3,t2
  7  where t1.object_id=to_number(t3.object_id)
  8  and t2.object_id=to_number(t3.object_id)
  9  and ( t1.object_id in (108,1080,10800) or t2.object_id in(108,1080,10800)) ;

 OBJECT_ID OBJECT_NAME                                                                                                                      STATUS  OWNER
---------- -------------------------------------------------------------------------------------------------------------------------------- ------- ------------------------------
       108 I_ACCESS1                                                                                                                        VALID   SYS
      1080 I_METAPATHMAP$                                                                                                                   VALID   SYS
     10800 KU$_PLUGTS_CHECKPL_VIEW                                                                                                          VALID   SYS

SQL> @allstat

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID  0pm99r7nvp84r, child number 0
-------------------------------------
select t1.object_id, t1.object_name, t3.status, t2.owner from t1,t3,t2
where t1.object_id=to_number(t3.object_id) and
t2.object_id=to_number(t3.object_id) and ( t1.object_id in
(108,1080,10800) or t2.object_id in(108,1080,10800))

Plan hash value: 3864638661
--CBO的表T1错误选择全表扫描
-----------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                       | Name             | Starts | E-Rows | A-Rows |   A-Time   | Buffers |  OMem |  1Mem | Used-Mem |
-----------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                |                  |      1 |        |      3 |00:00:00.07 |    2268 |       |       |          |
|   1 |  CONCATENATION                  |                  |      1 |        |      3 |00:00:00.07 |    2268 |       |       |          |
|*  2 |   HASH JOIN                     |                  |      1 |      3 |      3 |00:00:00.06 |    1136 |   843K|   843K|  611K (0)|
|   3 |    NESTED LOOPS                 |                  |      1 |      3 |      3 |00:00:00.01 |      16 |       |       |          |
|   4 |     INLIST ITERATOR             |                  |      1 |        |      3 |00:00:00.01 |       8 |       |       |          |
|   5 |      TABLE ACCESS BY INDEX ROWID| T2               |      3 |      3 |      3 |00:00:00.01 |       8 |       |       |          |
|*  6 |       INDEX RANGE SCAN          | IDX_T2_OBJECT_ID |      3 |      3 |      3 |00:00:00.01 |       5 |       |       |          |
|   7 |     TABLE ACCESS BY INDEX ROWID | T3               |      3 |      1 |      3 |00:00:00.01 |       8 |       |       |          |
|*  8 |      INDEX RANGE SCAN           | IDX_T3_OBJECT_ID |      3 |      1 |      3 |00:00:00.01 |       5 |       |       |          |
|   9 |    TABLE ACCESS FULL            | T1               |      1 |  81018 |  75253 |00:00:00.02 |    1120 |       |       |          |
|  10 |   NESTED LOOPS                  |                  |      1 |        |      0 |00:00:00.01 |    1132 |       |       |          |
|  11 |    NESTED LOOPS                 |                  |      1 |      3 |      0 |00:00:00.01 |    1132 |       |       |          |
|  12 |     NESTED LOOPS                |                  |      1 |      3 |      3 |00:00:00.01 |    1127 |       |       |          |
|* 13 |      TABLE ACCESS FULL          | T1               |      1 |      3 |      3 |00:00:00.01 |    1119 |       |       |          |
|  14 |      TABLE ACCESS BY INDEX ROWID| T3               |      3 |      1 |      3 |00:00:00.01 |       8 |       |       |          |
|* 15 |       INDEX RANGE SCAN          | IDX_T3_OBJECT_ID |      3 |      1 |      3 |00:00:00.01 |       5 |       |       |          |
|* 16 |     INDEX RANGE SCAN            | IDX_T2_OBJECT_ID |      3 |      1 |      0 |00:00:00.01 |       5 |       |       |          |
|  17 |    TABLE ACCESS BY INDEX ROWID  | T2               |      0 |      1 |      0 |00:00:00.01 |       0 |       |       |          |
-----------------------------------------------------------------------------------------------------------------------------------------

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

   2 - access("T1"."OBJECT_ID"=TO_NUMBER("OBJECT_ID"))
   6 - access(("T2"."OBJECT_ID"=108 OR "T2"."OBJECT_ID"=1080 OR "T2"."OBJECT_ID"=10800))
   8 - access("T2"."OBJECT_ID"="T3"."SYS_NC00016$")
  13 - filter(("T1"."OBJECT_ID"=108 OR "T1"."OBJECT_ID"=1080 OR "T1"."OBJECT_ID"=10800))
  15 - access("T1"."OBJECT_ID"="T3"."SYS_NC00016$")
  16 - access("T2"."OBJECT_ID"="T3"."SYS_NC00016$")
       filter((LNNVL("T2"."OBJECT_ID"=108) AND LNNVL("T2"."OBJECT_ID"=1080) AND LNNVL("T2"."OBJECT_ID"=10800)))

Note
-----
   - dynamic sampling used for this statement (level=2)


47 rows selected.


--由于对表T1到T4的列object_id分布我们都清楚,都中唯一值,过滤性很强
所以可以采用人工HINT去干预,也预示着我们在用to_char,to_number对列值进行转换连接的SQL,
需谨慎,极易造成执行计划的不稳定以及耗能过高;
--加入T1表的OBJECT_ID索引HINT纠正
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID  2qfqkjg1fzy0k, child number 0
-------------------------------------
select   /*+ index(t1(object_id) */ t1.object_id, t1.object_name,
t3.status, t2.owner from t1,t3,t2 where
t1.object_id=to_number(t3.object_id) and
t2.object_id=to_number(t3.object_id) and ( t1.object_id in
(108,1080,10800) or t2.object_id in(108,1080,10800))

Plan hash value: 3376884351

---------------------------------------------------------------------------------------------------------------
| Id  | Operation                        | Name             | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
---------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                 |                  |      1 |        |      3 |00:00:00.01 |      48 |
|   1 |  CONCATENATION                   |                  |      1 |        |      3 |00:00:00.01 |      48 |
|   2 |   NESTED LOOPS                   |                  |      1 |        |      3 |00:00:00.01 |      27 |
|   3 |    NESTED LOOPS                  |                  |      1 |      3 |      3 |00:00:00.01 |      24 |
|   4 |     NESTED LOOPS                 |                  |      1 |      3 |      3 |00:00:00.01 |      18 |
|   5 |      INLIST ITERATOR             |                  |      1 |        |      3 |00:00:00.01 |       9 |
|   6 |       TABLE ACCESS BY INDEX ROWID| T2               |      3 |      3 |      3 |00:00:00.01 |       9 |
|*  7 |        INDEX RANGE SCAN          | IDX_T2_OBJECT_ID |      3 |      3 |      3 |00:00:00.01 |       6 |
|   8 |      TABLE ACCESS BY INDEX ROWID | T3               |      3 |      1 |      3 |00:00:00.01 |       9 |
|*  9 |       INDEX RANGE SCAN           | IDX_T3_OBJECT_ID |      3 |      1 |      3 |00:00:00.01 |       6 |
|* 10 |     INDEX RANGE SCAN             | IDX_T1_OBJECT_ID |      3 |      1 |      3 |00:00:00.01 |       6 |
|  11 |    TABLE ACCESS BY INDEX ROWID   | T1               |      3 |      1 |      3 |00:00:00.01 |       3 |
|  12 |   NESTED LOOPS                   |                  |      1 |        |      0 |00:00:00.01 |      21 |
|  13 |    NESTED LOOPS                  |                  |      1 |      3 |      0 |00:00:00.01 |      21 |
|  14 |     NESTED LOOPS                 |                  |      1 |      3 |      3 |00:00:00.01 |      16 |
|  15 |      INLIST ITERATOR             |                  |      1 |        |      3 |00:00:00.01 |       8 |
|  16 |       TABLE ACCESS BY INDEX ROWID| T1               |      3 |      3 |      3 |00:00:00.01 |       8 |
|* 17 |        INDEX RANGE SCAN          | IDX_T1_OBJECT_ID |      3 |      3 |      3 |00:00:00.01 |       5 |
|  18 |      TABLE ACCESS BY INDEX ROWID | T3               |      3 |      1 |      3 |00:00:00.01 |       8 |
|* 19 |       INDEX RANGE SCAN           | IDX_T3_OBJECT_ID |      3 |      1 |      3 |00:00:00.01 |       5 |
|* 20 |     INDEX RANGE SCAN             | IDX_T2_OBJECT_ID |      3 |      1 |      0 |00:00:00.01 |       5 |
|  21 |    TABLE ACCESS BY INDEX ROWID   | T2               |      0 |      1 |      0 |00:00:00.01 |       0 |
---------------------------------------------------------------------------------------------------------------

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

   7 - access(("T2"."OBJECT_ID"=108 OR "T2"."OBJECT_ID"=1080 OR "T2"."OBJECT_ID"=10800))
   9 - access("T2"."OBJECT_ID"="T3"."SYS_NC00016$")
  10 - access("T1"."OBJECT_ID"="T3"."SYS_NC00016$")
  17 - access(("T1"."OBJECT_ID"=108 OR "T1"."OBJECT_ID"=1080 OR "T1"."OBJECT_ID"=10800))
  19 - access("T1"."OBJECT_ID"="T3"."SYS_NC00016$")
  20 - access("T2"."OBJECT_ID"="T3"."SYS_NC00016$")
       filter((LNNVL("T2"."OBJECT_ID"=108) AND LNNVL("T2"."OBJECT_ID"=1080) AND
              LNNVL("T2"."OBJECT_ID"=10800)))


49 rows selected.

SQL> 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值