【Oracle】查询条件中出现to_number报错,用Hint之后改变运行结果

前言:
以前一直觉得Hint加了不会影响sql执行状态或者结果,然后其实是会的。
因为涉及到很多的知识点,研究了还是挺有意义的。在这里探讨一下。

先看现象:
  1. --11.2.0.4.0
  2. SYS@proc> select name from v$parameter where name='db_block_size' and to_number(value)=8192;
  3. select name from v$parameter where name='db_block_size' and to_number(value)=8192
  4.                                                             *
  5. ERROR at line 1:
  6. ORA-01722: invalid number


  7. SYS@proc> explain plan for
  8.   2 select name from v$parameter where name='db_block_size' and to_number(value)=8192;

  9. Explained.

  10. SYS@proc> select * from table(dbms_xplan.display());

  11. PLAN_TABLE_OUTPUT
  12. --------------------------------------------------------------------------------
  13. Plan hash value: 1128103955

  14. -------------------------------------------------------------------------------
  15. | Id | Operation         | Name     | Rows  | Bytes   | Cost (%CPU)| Time     |
  16. -------------------------------------------------------------------------------
  17. |  0 | SELECT STATEMENT  |          |     1 |    2115 |     0   (0)| 00:00:01 |
  18. |* 1 |  HASH JOIN        |          |     1 |    2115 |     0   (0)| 00:00:01 |
  19. |* 2 |   FIXED TABLE FULL| X$KSPPI  |     1 |      81 |     0   (0)| 00:00:01 |
  20. |* 3 |   FIXED TABLE FULL| X$KSPPCV |     1 |    2034 |     0   (0)| 00:00:01 |
  21. -------------------------------------------------------------------------------


  22. PLAN_TABLE_OUTPUT
  23. --------------------------------------------------------------------------------
  24. Predicate Information (identified by operation id):
  25. ---------------------------------------------------

  26.    1 - access("X"."INDX"="Y"."INDX")
  27.        filter(TRANSLATE("KSPPINM",'_','#') NOT LIKE '#%' OR
  28.      "KSPPSTDF"='FALSE' OR BITAND("KSPPSTVF",5)>0)
  29.    2 - filter("KSPPINM"='db_block_size' AND
  30.      "X"."INST_ID"=USERENV('INSTANCE') AND BITAND("KSPPIFLG",268435456)=0
  31.      AND TRANSLATE("KSPPINM",'_','#') NOT LIKE '##%')
  32.    3 - filter(TO_NUMBER("KSPPSTVL")=8192)

  33. 21 rows selected.
  1. ---12C---
  2. SQL> select name from v$parameter where name='db_block_size' and to_number(value)=8192;

  3. NAME
  4. --------------------------------
  5. db_block_size
  6. SQL> set autotrace traceonly
  7. SQL> select name from v$parameter where name='db_block_size' and to_number(value)=8192;

  8. 执行计划
  9. ------------------------------------------------------------
  10. Plan hash value: 185276389

  11. ------------------------------------------------------------------------------------------------
  12. | Id    | Operation                | Name             | Rows  | Bytes  | Cost (%CPU)| Time     |
  13. ------------------------------------------------------------------------------------------------
  14. |     0 | SELECT STATEMENT         |                  |     1 |     55 |     0   (0)| 00:00:01 |
  15. |     1 |  NESTED LOOPS            |                  |     1 |     55 |     0   (0)| 00:00:01 |
  16. |*    2 |   FIXED TABLE FIXED INDEX| X$KSPPI (ind:1)  |     1 |     37 |     0   (0)| 00:00:01 |
  17. |*    3 |   FIXED TABLE FIXED INDEX| X$KSPPCV (ind:1) |     1 |     18 |     0   (0)| 00:00:01 |
  18. ------------------------------------------------------------------------------------------------

  19. Predicate Information (identified by operation id):
  20. ---------------------------------------------------

  21.     2 - filter("KSPPIM"='db_block_size' AND TRANSLATE("KSPPINM",'_','$') NOT LIKE
  22.              '$$%' AND "X"."INST_ID"=USERENV('INSTANCE') AND BITAND("KSPPIFLG",268435456)=0)
  23.     3 - filter("X"."INDX"="Y"."INDX" AND TO_NUMBER("KSPPSTVL")=8192 AND
  24.              (TRANSLATE("KSPPINM",'_','$') NOT LIKE '$%' OR "KSPPSTDF"='FALSE' OR
  25.              BITAND("KSPPSTVF",5>0))
12c可以正常运行不报错,但是11g里边却是报了错误。
原因是因为11g的执行计划id为3的谓词条件为"filter ( TO_NUMBER ( "KSPPSTVL" ) = 8192 ) ",是在X$KSPPCV上过滤的,自然含有不能to_number的值,比如含有字母的字符串。
12C做了优化可以运行,这条语句在11G运行不了,需要构造子查询/视图与外部查询,并加一个连接条件,才能尝试不用谓词推入。

实验过程:
这里将sql改写:
  1. SYS@proc> select value from (select value from v$parameter p where p.name='db_block_size') a where to_number(a.value)=8192;
  2. select value from (select value from v$parameter p where p.name='db_block_size') a where to_number(a.value)=8192
  3.                                                                                          *
  4. ERROR at line 1:
  5. ORA-01722: invalid number


  6. SYS@proc> select value from v$parameter p where p.name='db_block_size';

  7. VALUE
  8. --------------------------------------------------------------------------------
  9. 8192
从结果看,运行还是出现错误,但是" select value from v$parameter p where p . name = 'db_block_size' ; "执行结果只有一个"8192",不应该出现ora1722错误。
看一眼执行计划就明白了:
  1. SYS@proc> explain plan for select value from (select value from v$parameter p where p.name='db_block_size') a where to_number(a.value)=8192;

  2. Explained.

  3. SYS@proc> set linesize 200
  4. SYS@proc> select * from table(dbms_xplan.display());

  5. PLAN_TABLE_OUTPUT
  6. ------------------------------------------------------------------------------------------
  7. Plan hash value: 1128103955

  8. -------------------------------------------------------------------------------
  9. | Id | Operation         | Name     | Rows  | Bytes   | Cost (%CPU)| Time     |
  10. -------------------------------------------------------------------------------
  11. || SELECT STATEMENT  |          |     1 |    2115 |     0   (0)| 00:00:01 |
  12. |* 1 |  HASH JOIN        |          |     1 |    2115 |     0   (0)| 00:00:01 |
  13. |* 2 |   FIXED TABLE FULL| X$KSPPI  |     1 |      81 |     0   (0)| 00:00:01 |
  14. |* 3 |   FIXED TABLE FULL| X$KSPPCV |     1 |    2034 |     0   (0)| 00:00:01 |
  15. -------------------------------------------------------------------------------


  16. PLAN_TABLE_OUTPUT
  17. ------------------------------------------------------------------------------------------
  18. Predicate Information (identified by operation id):
  19. ---------------------------------------------------

  20.    1 - access("X"."INDX"="Y"."INDX")
  21.        filter(TRANSLATE("KSPPINM",'_','#') NOT LIKE '#%' OR
  22.      "KSPPSTDF"='FALSE' OR BITAND("KSPPSTVF",5)>0)
  23.    2 - filter("KSPPINM"='db_block_size' AND
  24.      "X"."INST_ID"=USERENV('INSTANCE') AND BITAND("KSPPIFLG",268435456)=0
  25.      AND TRANSLATE("KSPPINM",'_','#') NOT LIKE '##%')
  26.    3 - filter(TO_NUMBER("KSPPSTVL")=8192)

  27. 21 rows selected.
尝试加Hint不做谓词推入,不过前提是不做视图合并:
  1. SYS@proc> select /*+ no_merge(a) no_push_pred(a) */value from (select value from v$parameter p where p.name='db_block_size') a where to_number(a.value)=8192;
  2. select /*+ no_merge(a) */value from (select value from v$parameter p where p.name='db_block_size') a where to_number(a.value)=8192
  3.                                                                                                            *
  4. ERROR at line 1:
  5. ORA-01722: invalid number


  6. SYS@proc> explain plan for select /*+ no_merge(a) no_push_pred(a) */value from (select value from v$parameter p where p.name='db_block_size') a where to_number(a.value)=8192;

  7. Explained.

  8. SYS@proc> select * from table(dbms_xplain.display());
  9. select * from table(dbms_xplain.display())
  10.                     *
  11. ERROR at line 1:
  12. ORA-00904: "DBMS_XPLAIN"."DISPLAY": invalid identifier


  13. SYS@proc> select * from table(dbms_xplan.display());

  14. PLAN_TABLE_OUTPUT
  15. -------------------------------------------------------------------------------------------
  16. Plan hash value: 3035854849

  17. -------------------------------------------------------------------------------
  18. | Id | Operation          | Name     | Rows | Bytes | Cost (%CPU)|  Time     |
  19. -------------------------------------------------------------------------------
  20. |  0 | SELECT STATEMENT   |          |    1 |  2002 |    0    (0)|  00:00:01 |
  21. |  1 |  VIEW              |          |    1 |  2002 |    0    (0)|  00:00:01 |
  22. |* 2 |   HASH JOIN        |          |    1 |  2115 |    0    (0)|  00:00:01 |
  23. |* 3 |    FIXED TABLE FULL| X$KSPPI  |    1 |    81 |    0    (0)|  00:00:01 |
  24. |* 4 |    FIXED TABLE FULL| X$KSPPCV |    1 |  2034 |    0    (0)|  00:00:01 |
  25. -------------------------------------------------------------------------------

  26. PLAN_TABLE_OUTPUT
  27. ------------------------------------------------------------------------------------------

  28. Predicate Information (identified by operation id):
  29. ---------------------------------------------------

  30.    2 - access("X"."INDX"="Y"."INDX")
  31.        filter(TRANSLATE("KSPPINM",'_','#') NOT LIKE '#%' OR
  32.      "KSPPSTDF"='FALSE' OR BITAND("KSPPSTVF",5)>0)
  33.    3 - filter("KSPPINM"='db_block_size' AND
  34.      "X"."INST_ID"=USERENV('INSTANCE') AND BITAND("KSPPIFLG",268435456)=0
  35.      AND TRANSLATE("KSPPINM",'_','#') NOT LIKE '##%')
  36.    4 - filter(TO_NUMBER("KSPPSTVL")=8192)

  37. 22 rows selected.
这里的no_merge的hint是生效了,不过no_push_pred却没有。
原因是这个sql的改写也有问题,push_pred/no_push_pred的应用场景是视图和外部查询之间有连接条件,对于push_pred,优化器会把原本处于该视图外部查询中和该视图之间的连接条件推入到该视图定义的SQL语句内部,这样做是为了能使用上该视图内部相关基表的索引,进而走出基于索引的嵌套循环连接。
需要连接条件的话,要有构造另外一张辅助表:
  1. SYS@proc> create table test(id int);

  2. Table created.

  3. SYS@proc> insert into test values(null);

  4. 1 row created.

  5. SYS@proc> commit;

  6. Commit complete.
sql改写:select a.value from (select value from v$parameter p where p.name='db_block_size') a,test b where to_number(id||a.value)=8192;
该sql的执行计划以及执行结果如下:
  1. SYS@proc> select a.value from (select value from v$parameter p where p.name='db_block_size') a,test b where to_number(id||a.value)=8192;
  2. select a.value from (select value from v$parameter p where p.name='db_block_size') a,test b where to_number(id||a.value)=8192
  3.                                                                                                                *
  4. ERROR at line 1:
  5. ORA-01722: invalid number


  6. SYS@proc> explain plan for select a.value from (select value from v$parameter p where p.name='db_block_size') a,test b where to_number(id||a.value)=8192;

  7. Explained.

  8. SYS@proc> select * from table(dbms_xplan.display());

  9. PLAN_TABLE_OUTPUT
  10. -------------------------------------------------------------------------------------------
  11. Plan hash value: 662541633

  12. --------------------------------------------------------------------------------
  13. | Id | Operation           | Name     | Rows  | Bytes | Cost (%CPU)| Time     |
  14. --------------------------------------------------------------------------------
  15. |  0 | SELECT STATEMENT    |          |     1 |  2128 |   2     (0)| 00:00:01 |
  16. |* 1 |  HASH JOIN          |          |     1 |  2128 |   2     (0)| 00:00:01 |
  17. |  2 |   NESTED LOOPS      |          |     1 |  2047 |   2     (0)| 00:00:01 |
  18. |  3 |    TABLE ACCESS FULL| TEST     |     1 |    13 |   2     (0)| 00:00:01 |
  19. |* 4 |    FIXED TABLE FULL | X$KSPPCV |     1 |  2034 |   0     (0)| 00:00:01 |
  20. |* 5 |   FIXED TABLE FULL  | X$KSPPI  |     1 |    81 |   0     (0)| 00:00:01 |
  21. --------------------------------------------------------------------------------

  22. Predicate Information (identified by operation id):
  23. ---------------------------------------------------

  24.    1 - access("X"."INDX"="Y"."INDX")
  25.        filter(TRANSLATE("KSPPINM",'_','#') NOT LIKE '#%' OR
  26.      "KSPPSTDF"='FALSE' OR BITAND("KSPPSTVF",5)>0)
  27.    4 - filter(TO_NUMBER(TO_CHAR("ID")||"KSPPSTVL")=8192)
  28.    5 - filter("KSPPINM"='db_block_size' AND
  29.      "X"."INST_ID"=USERENV('INSTANCE') AND BITAND("KSPPIFLG",268435456)=0
  30.      AND TRANSLATE("KSPPINM",'_','#') NOT LIKE '##%')

  31. Note
  32. -----
  33.    - dynamic sampling used for this statement (level=2)

  34. 27 rows selected.
从执行计划上看,id为4的那步,在X$KSPPCV过滤 to_number ( id | | a . value ) = 8192的数据,自然是有问题的。
添加Hint强制不做谓词推入,如下:
  1. SYS@proc> select /*+ no_merge(a) no_push_pred(a)*/ a.value from (select value from v$parameter p where p.name='db_block_size') a,test b where to_number(id||a.value)=8192;

  2. VALUE
  3. -----------------------------------------------------------------------------------------------
  4. 8192

  5. SYS@proc> explain plan for select /*+ no_merge(a) no_push_pred(a)*/ a.value from (select value from v$parameter p where p.name='db_block_size') a,test b where to_number(id||a.value)=8192;

  6. Explained.

  7. SYS@proc> select * from table(dbms_xplan.display());

  8. PLAN_TABLE_OUTPUT
  9. ------------------------------------------------------------------------------------------------
  10. Plan hash value: 742769369

  11. ------------------------------------------------------------------------------
  12. | Id | Operation           | Name     | Rows | Bytes | Cost (%CPU)| Time     |
  13. ------------------------------------------------------------------------------
  14. |  0 | SELECT STATEMENT    |          |    1 |  2015 |   2     (0)| 00:00:01 |
  15. |  1 |  NESTED LOOPS       |          |    1 |  2015 |   2     (0)| 00:00:01 |
  16. |  2 |   VIEW              |          |    1 |  2002 |   0     (0)| 00:00:01 |
  17. |* 3 |    HASH JOIN        |          |    1 |  2115 |   0     (0)| 00:00:01 |
  18. |* 4 |     FIXED TABLE FULL| X$KSPPI  |    1 |    81 |   0     (0)| 00:00:01 |
  19. |  5 |     FIXED TABLE FULL| X$KSPPCV |  100 |   198K|   0     (0)| 00:00:01 |
  20. |* 6 |   TABLE ACCESS FULL | TEST     |    1 |    13 |   2     (0)| 00:00:01 |
  21. ------------------------------------------------------------------------------

  22. Predicate Information (identified by operation id):
  23. ---------------------------------------------------

  24.    3 - access("X"."INDX"="Y"."INDX")
  25.        filter(TRANSLATE("KSPPINM",'_','#') NOT LIKE '#%' OR
  26.      "KSPPSTDF"='FALSE' OR BITAND("KSPPSTVF",5)>0)
  27.    4 - filter("KSPPINM"='db_block_size' AND
  28.      "X"."INST_ID"=USERENV('INSTANCE') AND BITAND("KSPPIFLG",268435456)=0
  29.      AND TRANSLATE("KSPPINM",'_','#') NOT LIKE '##%')
  30.    6 - filter(TO_NUMBER(TO_CHAR("ID")||"A"."VALUE")=8192)

  31. Note
  32. -----
  33.    - dynamic sampling used for this statement (level=2)

  34. 28 rows selected.
执行计划上看 ,做to_number转换的对象是在test表上,自然就是可以运行出结果了。
PS:

该语句也是一样的: select a . value from ( select /*+ no_merge no_push_pred*/ value from v$parameter p where p . name = 'db_block_size' ) a , test b where to_number ( id | | a . value ) = 8192 ;

问题延伸:
  1. SYS@proc> create table t as select * from v$parameter;

  2. Table created.

  3. SYS@proc> select value from t where name='db_block_size' and to_number(value)=8192;

  4. VALUE
  5. --------------------------------------------------------------------------------
  6. 8192

  7. SYS@proc> select value from v$parameter where name='db_block_size' and to_number(value)=8192;
  8. select value from v$parameter where name='db_block_size' and to_number(value)=8192
  9.                                                              *
  10. ERROR at line 1:
  11. ORA-01722: invalid number
为什么语句“ select value from t where name = 'db_block_size' and to_number ( value ) = 8192 ; ”执行成功,换成v$parameter却报错。
该问题在另外一篇做研究。
这里附上链接http://blog.itpub.net/30174570/viewspace-2148607/


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

转载于:http://blog.itpub.net/30174570/viewspace-2148592/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值