Hi Team,
The data in the table is with varchar2 column is :
ID
---
43567
77866
256d5
I have to find out numbers which are greater than 40,000
I used below query but getting invalid number error:
select al.my_num from
(select to_number(id,'99999') my_num from my_num_other where replace(translate(id,'0123456789','0000000000'),'0','') is null) al
where al.my_num>40000
ORA-01722: invalid number
01722. 00000 - "invalid number"
*Cause: The specified number was invalid.
*Action: Specify a valid number.
This is because we generally merge queries together - you dont get to choose *how* and in what order things are executed, eg
SQL> drop table my_num_other purge;
Table dropped.
SQL>
SQL> create table my_num_other as
2 select 'X' id from dual
3 union all
4 select to_char(rownum+30500) from dual connect by level <= 10000;
Table created.
SQL>
SQL> select al.my_num from
2 (select to_number(id,'99999') my_num
3 from my_num_other
4 where replace(translate(id,'0123456789','0000000000'),'0','') is null) al
5 where al.my_num>40000;
where al.my_num>40000
*
ERROR at line 5:
ORA-01722: invalid number
SQL>
SQL> set autotrace traceonly explain
SQL>
SQL> select al.my_num from
2 (select to_number(id,'99999') my_num
3 from my_num_other
4 where replace(translate(id,'0123456789','0000000000'),'0','') is null) al
5 where al.my_num>40000;
Execution Plan
----------------------------------------------------------
Plan hash value: 1649396075
----------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 25 | 150 | 5 (0)| 00:00:01 |
|* 1 | TABLE ACCESS FULL| MY_NUM_OTHER | 25 | 150 | 5 (0)| 00:00:01 |
----------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(TO_NUMBER("ID",'99999')>40000 AND
REPLACE(TRANSLATE("ID",'0123456789','0000000000'),'0','') IS NULL)
Notice how we just folded everything into one statement, and in this case, the "to_number" came first. You can mitigate this using something like a global temporary table, or a something like below
SQL> with check_this_first as
2 (select /*+ materialize */ to_number(id,'99999') my_num
3 from my_num_other
4 where replace(translate(id,'0123456789','0000000000'),'0','') is null
5 )
6 select * from check_this_first
7 where my_num>40000;
MY_NUM
----------
40001
40002
40003
40004
40005
40006
...
500 rows selected.
SQL>
I wouldnt recommend the above as a "solution", because who knows what changes we'll make in the next version of Oracle and the optimizer might be smarter and shuffle things around. I'm showing it here as a demo of how you would do the global temp table approach, ie, load the data, and *then* do the check.
Alternatively, create your own to_number functin, ie
function my_to_number(p_str varchar2) return number is
x number;
begin
x := to_number(p_str);
return x;
exception
when others then return null;
end;