这是个oracle 同表查询总嵌套子查询的执行计划问题。
一张表 xxx (文中执行计划部分打码), 有个yyy varchar2的字段,里边是数字,含有特殊的admin 需求,将yyy转为数字型的,去除admin,然后 过滤 yyy>30000 的结果集。
执行后报出 无效数字的错误 (单独执行子查询是没有问题的)
出现无效数字的问题,首先确定的是 数据类型不匹配 分析上述语句 , 就是对同一张表做的嵌套查询, 但是单独执行子查询是没有问题的。也就是说 to_number(yyy) 里边的字符是可以正常转成数字的, 又加了外层的查询后出的问题。
出现这种情况开始我一度怀疑是 to_number() 函数的问题,查看了官方文档以后,也没看出个所以然。
这其中我忽略了一个解决sql问题的很重要的步骤 就是查看 sql 执行计划 ,浪费了些许时间在网上所查结果,半天也一无所获,甚至被误导方向。
oracle sql develope 工具分析如下:
上述的执行计划可以看到,同一张表中做嵌套子查询的时候 oracle 把子查询与外层查询的条件放到了一块 这肯定会出问题的,因为字段里又admin等特殊的字段,执行to_number 的时候会出现问题。
为什么放在一起就出问题呢? 之所以做了个嵌套查询就是想有个我们想要的查询条件顺序,即 第一步过滤 先将yyy字段中的不能正常转换的 “admin”过滤掉,第二部在执行 数值大小比较。 但是oacle 的执行计划中,对于同一张表的这种查询是不支持的,这就需要我们用特殊的手段。
既然知道了问题的原因所在,那么解决问题就不难了。
着重点就是让查询条件按照整齐而的步骤执行
这里有三种解决办法
即aaa表中的结果是不含 admin的。
然后再查aaa表 select id from aaa where id < 30000;即可得到最终结果。
比较笨的方法, 但是也是可以解决问题的。先来看下该语句的执行计划
可以看到这里的 to_number函数中 用了REGEXP_REPLACE 过滤, 非数字型的直接替换为空, 所以在这里条件执行的顺序也就无关紧要了,在to_number中的数值始终是可以转成数值型的数据,不会出现转换异常的无效数字错误。 并且 条件 xxx.yyy <>‘admin’ 也可以去掉了, 因为在函数REGEXP_REPLACE 中已经过滤掉了。
就是人为的干预oracle的条件执行顺序(执行计划)
select * from ( select yyy as userID from xxx where xxx.yyy <>'admin' ) a group by a.userID having a.userID >= 30000执行计划:
即加入了 group by … having 语句 , 就是保证了 <> admin 这句的执行,然后再 having 执行基于上个过滤条件过滤的结果 ,再进行过滤。
最终就是保证了 查询字段 在 to_number() 函数(隐式或显示)的时候不发生转换异常。
遇到这种同一张表的多个条件查询,并且各条件之间有前后顺序依赖的时候,不能简单的用 嵌套子查询的方式。
推荐用 方法二 (正则替换), 方法三 group by 当然正则替换可能写起来稍有点复杂。 group by 方式看起来更简单一些。具体要根据实际情况去选择一个合适的方式。
总之找到问题的根结才能对症下药。