首页 > 数据库 >Oracle优化器对谓词顺序处理的一个场景

Oracle优化器对谓词顺序处理的一个场景

时间:2023-06-19 11:36:54浏览次数:45  
标签:场景 number 谓词 Oracle c2 c1 执行 where


最近听了个讲座,其中介绍到了Oracle的谓词,原始版本的例子,如下所示,从数据上能看到,c1='3'的时候,c2的值是个字符串类型的数字,

SQL> create table test(c1 char(1), c2 varchar2(1));
Table created.


SQL> insert into test values('1', 'A');
1 row created.


SQL> insert into test values('2', 'A');
1 row created.


SQL> insert into test values('3', '3');
1 row created.

如下测试,在Oracle 11g执行,提示错误,认为to_number的变量存在非法的数值类型,

SQL> select to_number(c2) as value, c1 from test where c1='3' and to_number(c2)>2;
select to_number(c2) as value, c1 from test where c1='3' and to_number(c2)>2
                                                              *
ERROR at line 1:
ORA-01722: invalid number

之所以提示这个,我们能从执行计划中找到原因,可以看到,谓词条件先执行的是to_number(c2),

---------------------------------------------------------------------------
| Id  | Operation         | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |       |       |       |     3 (100)|          |
|*  1 |  TABLE ACCESS FULL| TEST  |     1 |     5 |     3   (0)| 00:00:01 |
---------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter((TO_NUMBER("C2")>2 AND "C1"='3'))

因为我们知道,c1='1'、c1='2'的时候,c2是'A',to_number('A')就会提示invalid number,

SQL> select to_number('A') from dual;
select to_number('A') from dual
                 *
ERROR at line 1:
ORA-01722: invalid number

从这儿我们还可以知道一点,就是Oracle的where各条件的位置其实并不重要,准确来说,在CBO优化器模式下,究竟是c1='3' and to_number(c2)>2,还是to_number(c2)>2 and c1='3',都会经过Oracle的查询改写,给出真正执行的条件顺序,可以从10053找到他的查询改写。如上例子中,按照语义,就是先执行c1='3',按说他得到的c2就是数值类型的字符串,可以使用to_number函数,但实际上无论where中怎么写,都是按照to_number(c2)>2 and c1='3'来执行,因此报错。但是如果使用RBO优化器,where条件的顺序就可能影响执行计划。

说明了Oracle真正执行的语句是经过他的优化器调整的“他认为”的路径,但这个路径很可能导致语句执行错误,如上的示例中,如果先执行c1='3',再执行to_number(c2),应该就可以。

如果按照语义,改造一下,子查询中指定where c1='3',打算得到的c2都是数值的字符串类型,然后在外层where value(to_number(c2))>2,但是仍然报"ORA-01722: invalid number",

SQL> select * from (select to_number(c2) as value, c1 from test where c1='3') where value>2;

从执行计划,能看到谓词条件仍是如下这个,虽然to_number(c2)在外层,但实际执行时,Oracle通过谓词推入,将外层条件和内层条件进行了整合,

---------------------------------------------------------------------------
| Id  | Operation         | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |       |       |       |     3 (100)|          |
|*  1 |  TABLE ACCESS FULL| TEST  |     1 |     5 |     3   (0)| 00:00:01 |
---------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter((TO_NUMBER("C2")>2 AND "C1"='3'))

针对这个问题,可以改造如下,子查询中增加rownum>=1,避免谓词推入,

SQL> select * from (select to_number(c2) as value, c1 from test where c1='3' and rownum>=1) where value>2;

执行计划的谓词条件就改成了如下,并未做谓词推入,而是按照我们的“语义”选择的执行路径,这样就可以先找到c1='3'的记录,他的c2是'3',就可以正常用to_number()函数了,

-------------------------------------------------------------------------------
| Id  | Operation             | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |       |       |       |     3 (100)|          |
|*  1 |  VIEW                 |       |     1 |       |     3   (0)| 00:00:01 |
|*  2 |   COUNT               |       |       |       |            |          |
|*  3 |    FILTER             |       |       |       |            |          |
|*  4 |     TABLE ACCESS FULL | TEST  |     1 |     5 |            | 00:00:01 |
-------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter("VALUE">2)
   3 - filter(ROWNUM>=1)
   4 - filter("C1"='3')

另外一种方式,就是采用ordered_predicates这个HINT,CBO下强制按照where条件书写顺序来执行,

select /*+ ordered_predicates */ to_number(c2) as value, c1 from test where c1='3' and to_number(c2)>2;
TO_NUMBER(C2) C
------------- -
            3 3

可以看到,谓词条件就改为,达到了我们需要让条件c1='3'先执行的目的,

1 - filter(("C1"='3' AND TO_NUMBER("C2")>2))

可能有细心的朋友发现,上述例子中c2的类型是char,如果是number或者varchar2,现象还相同么?

眼见为实,如果c2定义为number或者varchar2(1),

Oracle优化器对谓词顺序处理的一个场景_java

无论何种写法,都可以正常执行,

Oracle优化器对谓词顺序处理的一个场景_oracle_02

从谓词条件,能知道他的顺序就是我们需要的,先按照c1='3'进行过滤,他得到的c2,都是能使用to_number()函数的值,因此不会报错,

1 - filter(("C1"='3' AND TO_NUMBER("C2")>2))

说明Oracle在做查询转换的时候,针对char、varchar2、number不同的数据类型,可能设置了不同的优先级,才导致了不同的执行条件顺序。

从这个案例中,还可以提醒我们一点,就是字段的含义和类型应该保持一致,这是在数据库设计阶段需要注意的。例如c1存储的就是“数字”,如果定义为字符串类型,varchar2还好,char就出现了上述执行错误的场景,如果“数字”就使用数值类型number存储,就会绕过这个坑,同理,像“日期”用字符串类型存储存在相同的问题,可以参考《为什么日期不建议使用VARCHAR2或者NUMBER?》。

因此,有时一些表象问题背后,可能蕴藏着非规范的问题,或者说其实可以通过规范的设计和开发,避免这些“坑”,知其然更要知其所以然,更要从根本,解决碰到的问题。


标签:场景,number,谓词,Oracle,c2,c1,执行,where
From: https://blog.51cto.com/u_13950417/6512001

相关文章

  • Windows调试Oracle数据库问题的一些手段
    最近需要在Windows的跳板机上访问远程的Oracle数据库进行调试。Windows中只有个精简版OracleClient,以及PLSQLDeveloper客户端。如果用惯了Linux,再用Windows,确实不知所措。但这就是个从生疏到熟练的事儿,最近看的一本书中描述得很恰当,Themosteffectivewaytodealwithaworry......
  • 《Oracle Concept》第三章 - 4
    背景:按照《OracleConecpt》的结构一起了解Oracle数据库,这是学习Oracle从入门到精通的基础。本文主题:第三章《IndexesandIndex-OrganizedTables》-OverviewofIndexes。B树索引B树索引,是平衡树的缩写,他是数据库索引中最常用的一种类型。一个B树索引是一种将数值有序划分到不......
  • 调用拷贝构造函数的场景
    1.默认构造函数默认构造函数是一个系统生成的,参数列表和函数体都为空的函数。如果类中声明了默认构造函数(无论是否有参数),编译器便不再会为之生成隐含的构造函数。2.析构函数析构函数的函数列表为空,因此不能实现重载。可以显式调用析构函数。例如可以通过对象名等进行调用,obj......
  • Redis - 数据结构类型及使用场景详解
    一.简介Redis是由SalvatoreSanfilippo编写的一个key-value存储系统,是跨平台的非关系型数据库。Redis是一个开源的,使用C语言编写的,遵守BSD协议,支持网络,可基于内存,分布式,可选持久性的键值对(key-value)存储数据库,并且提供了多种语言的API。二.特性1.基于内存存储(不开启持久化的......
  • oracle固定执行计划
    文档课题:oracle固定执行计划.本文介绍oracle固定执行计划的2种方法,outline,SQLProfile,其中SQLProfile是重点。1、outline1.1、相关概念开发环境中SQL的执行无问题,但在生产环境执行效率却异常缓慢。若此时更改SQL需重新修改及编译源程序,成本非常高。此时可使用outline在不改变......
  • ORACLE如何找出视图依赖的对象和视图嵌套层数
    之前写过一篇文章“SQLServer如何找出视图依赖的对象和视图嵌套层数”,这里我介绍一下Oracle数据库中如何找出视图的依赖对象以及视图嵌套层数关系。主要通过DBA_DEPENDENCIES这个系统视图(这个系统视图中包含有对象的依赖关系数据)。另外,我们使用了Oracle的树形查询(层级查询)来展示这......
  • 后台用异步线程调用的场景与常用方式
    一.异步执行的场景:完成业务后,发短信、发邮件、微信公众号等消息推送提示的功能,可以采用异步执行。在导入数量量过大等情况下,可以使用异步导入的方式,提高导入时间等。...等等二.实现的方式:1.springboot中,进行线程池配置,然后用@Async标识异步执行方法即可,如下:(需要注意的@Enable......
  • 在 Cenntos6.8 下安装 Oracle11g
    安装所需文件如下1.一台装有CentOS 6.8x64的服务器(虚拟机也可)2. linux.x64_11gR2_database_1of2.zip3.linux.x64_11gR2_database_2of2.zip"系统要求如下1.SWAP分区大于3G1.Oracle安装目录剩余空间大于20G2.Centos6.x系统安装centos系统首先我们要安装一个带Xwi......
  • Oracle 扩容 SGA ORA-27104
    问题概述某客户一套19c生产环境在主机层面对内存进行了扩容,DBA随后对数据库的SGA的大小进行调整,调整完重启实例时报ORA-27104,无法正常启动实例。如下图所示,SGA原大小为8G,现调整为32G闭实例,再启动实例到nomount状态,提示ORA-27104报错: 问题原因查看数据库alert日志:提示‘Systemcann......
  • Oracle常用统计
     测试,这是测消息 1.按天selectto_char(t.STARTDATE+15/24,'YYYY-MM-DD')as天,sum(1)as数量fromHOLIDAYtgroupbyto_char(t.STARTDATE+15/24,'YYYY-MM-DD')--ORDERby天NULLS LAST;  selecttrunc(t.STARTDATE,'DD')as天,sum(1)as......