Hello Tom,
I'm obviously missing something here, but I can't figure out why
the ORA-01722: invalid number occurs.
Do you know why?
Thank you.
Connected to:
Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options
RSC101@ouds > create table test_tb
2 ( test_col varchar2(9) );
Table created.
RSC101@ouds > create table error_tb
2 ( error_col varchar2(9) );
Table created.
RSC101@ouds > insert into test_tb (test_col) values ('123456789');
1 row created.
RSC101@ouds > insert into test_tb (test_col) values ('x23456789');
1 row created.
RSC101@ouds > --
RSC101@ouds > insert into error_tb (error_col) values ('x23456789');
1 row created.
RSC101@ouds > commit;
Commit complete.
RSC101@ouds > create or replace view filter_errors_v as
2 select test_col
3 from test_tb
4 minus
5 select error_col
6 from error_tb;
View created.
RSC101@ouds >
RSC101@ouds > select * from filter_errors_v;
TEST_COL
---------
123456789
RSC101@ouds > select to_number(substr( test_col , 1, 1)) sub1
2 from filter_errors_v;
SUB1
----------
1
RSC101@ouds >
RSC101@ouds > select * from (
2 select to_number(substr( test_col , 1, 1)) sub1
3 from filter_errors_v
4 ) where sub1 > 0;
select to_number(substr( test_col , 1, 1)) sub1
*
ERROR at line 2:
ORA-01722: invalid number
RSC101@ouds >
because of predicate pushing and view merging. parenthesis do not cause set operations to happen one after the other or anything like that in sql - sql is not a procedural language.
select * from (
2 select to_number(substr( test_col , 1, 1)) sub1
3 from filter_errors_v
4 ) where sub1 > 0;
is the same as
select to_number(substr(test_col,1,1)) sub1
from filter_errors_v
where to_number(substr(test_col1,1)) > 0
is the same as
select to_number(substr(test_col,1,1)) sub1
from (select test_col
from test_tb
minus
select error_col test_col
from error_tb)
where to_number(substr(test_col1,1)) > 0
is the same as
select to_number( substr(test_col,1,1)) sub1
from test_tb
where to_number(substr(test_col,1,1)) > 0
minus
select substr(error_col,1,1)) sub1
from error_tb
where to_number(substr(test_col1,1)) > 0
You can see that in the plan:
ops$tkyte%ORA11GR2> set autotrace traceonly explain
ops$tkyte%ORA11GR2> select * from (
2 select to_number(substr( test_col , 1, 1)) sub1
3 from filter_errors_v
4 ) where sub1 > 0;
Execution Plan
----------------------------------------------------------
Plan hash value: 1655715634
----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 4 | 24 | 8 (25)| 00:00:01 |
| 1 | VIEW | FILTER_ERRORS_V | 4 | 24 | 8 (25)| 00:00:01 |
| 2 | MINUS | | | | | |
| 3 | SORT UNIQUE | | 4 | 24 | 4 (25)| 00:00:01 |
|* 4 | TABLE ACCESS FULL| TEST_TB | 4 | 24 | 3 (0)| 00:00:01 |
| 5 | SORT UNIQUE | | 4 | 24 | 4 (25)| 00:00:01 |
|* 6 | TABLE ACCESS FULL| ERROR_TB | 4 | 24 | 3 (0)| 00:00:01 |
----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
4 - filter(TO_NUMBER(SUBSTR("TEST_COL",1,1))>0)
6 - filter(TO_NUMBER(SUBSTR("ERROR_COL",1,1))>0)
In fact, if you were to run a 10053 optimizer trace on this, you would see in the tracefile:
Final query after transformations:******* UNPARSED QUERY IS *******
SELECT TO_NUMBER(SUBSTR("FILTER_ERRORS_V"."TEST_COL",1,1)) "SUB1"
FROM
(
(SELECT "TEST_TB"."TEST_COL" "TEST_COL"
FROM OPS$TKYTE."TEST_TB" "TEST_TB"
WHERE TO_NUMBER(SUBSTR("TEST_TB"."TEST_COL",1,1))>0)
MINUS
(SELECT "ERROR_TB"."ERROR_COL" "TEST_COL"
FROM OPS$TKYTE."ERROR_TB" "ERROR_TB"
WHERE TO_NUMBER(SUBSTR("ERROR_TB"."ERROR_COL",1,1))>0
)
) "FILTER_ERRORS_V"
see
http://www.oracle.com/technetwork/issue-archive/2005/05-mar/o25asktom-089621.html the section on "Views and Merging/Pushing" to read more about this.
ops$tkyte%ORA11GR2> select * from (
2 select case when substr(test_col,1,1) between '0' and '9' then to_number(substr( test_col , 1, 1)) end sub1
3 from filter_errors_v
4 ) where sub1 > 0;
SUB1
----------
1
Execution Plan
----------------------------------------------------------
Plan hash value: 1655715634
----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 6 | 8 (25)| 00:00:01 |
| 1 | VIEW | FILTER_ERRORS_V | 1 | 6 | 8 (25)| 00:00:01 |
| 2 | MINUS | | | | | |
| 3 | SORT UNIQUE | | 1 | 6 | 4 (25)| 00:00:01 |
|* 4 | TABLE ACCESS FULL| TEST_TB | 1 | 6 | 3 (0)| 00:00:01 |
| 5 | SORT UNIQUE | | 1 | 6 | 4 (25)| 00:00:01 |
|* 6 | TABLE ACCESS FULL| ERROR_TB | 1 | 6 | 3 (0)| 00:00:01 |
----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
4 - filter(CASE WHEN (SUBSTR("TEST_COL",1,1)>='0' AND
SUBSTR("TEST_COL",1,1)<='9') THEN TO_NUMBER(SUBSTR("TEST_COL",1,1)) END >0)
6 - filter(CASE WHEN (SUBSTR("ERROR_COL",1,1)>='0' AND
SUBSTR("ERROR_COL",1,1)<='9') THEN TO_NUMBER(SUBSTR("ERROR_COL",1,1)) END >0)
You'll need to use techniques like this anytime (everytime) you convert a string to a number (or date) and the string might not be convertible at some level.