This is a translation of my German article which was requested through “Google Translate” many times. It seems that throughout the years this error and its mostly simple causes haven’t lost their significance.
Here’s a sample scenario:
- A table’s VARCHAR2 column shall be searched for numeric entries;
- We use a query with a WHERE clause that constrains the scanned rows to only those containing numeric values.
- Despite that, “ORA-01722” is thrown.
So, what happened?
- The Oracle documentation states: “When comparing a character value with a numeric value, Oracle converts the character data to a numeric value.” Think of an implicit TO_NUMBER being applied to the column.
- This implicit TO_NUMBER might be applied before the filter predicates of your WHERE clause.
- This in turn leads to an error, when an alphanumeric entry is hit — even when this entry wouldn’t be part of the scanned set if the filters were applied first.
Example
Let’s create a simple table with a VARCHAR2 column that will hold numeric and alphanumeric values. To make it easier to distinguish between numeric and alphanumeric entries, a second column containing a type indicator is used:
CREATE TABLE t1( content_type VARCHAR2(1), mycontent VARCHAR2(10) ) / INSERT INTO t1 VALUES( 'N', '12345' ); INSERT INTO t1 VALUES( 'T', 'a1234' ); COMMIT /
Now, the following SQL will fail — and it’s pretty obvious, why:
SELECT count(*) FROM t1 WHERE mycontent > 1; FEHLER in Zeile 3: ORA-01722: invalid number
The WHERE clause used the numeric value 1. This will lead to an implicit TO_NUMBER being applied to the column “mycontent”. As soon as this implicit TO_NUMBER processes an alphanumeric Value, the query errs out.
Let’s try to avoid the problem by filtering out the alphanumeric entries using our type indicator:
SELECT count(*) FROM t1 WHERE mycontent > 1 AND content_type = 'N'; COUNT(*) ---------- 1
It works! … sometimes. Sometimes, not.
It is not guaranteed that Oracle will always apply our predicates in the given order. In more complex joins it’s not uncommon that the optimizer decides to apply the predicates in an order that might be performing better. In our case, when mycontent > 1
is applied before content_type = 'N'
, this will lead to errors. We can force this behaviour using an optimizer hint to apply the predicates in the given order:
SELECT /*+ ORDERED_PREDICATES */ count(*) FROM t1 WHERE mycontent > 1 AND content_type = 'N' ; ERROR: ORA-01722: invalid number
Since Oracle 10g, this scenario will happen in many cases, because the “CPU costing” of the optimizer can lead to a change in predicate order. In his blog, Jonathan Lewis discusses this behaviour and recommends to change your data model if this happens (see approach 3 below).
Counter measures
Approach 1 — smarter filtering: We filter for values containing digits only. This can be done with the SQL function translate
. For that we need another symbol that we’ll convert 1:1, because if we used an empty string for the conversion, everything would be converted to NULL. The following example assumed that there’s no “#” in the scanned column, so we use this character as a “dummy”:
SELECT ROWID, mycontent FROM t1 WHERE translate( mycontent, '#0123456789','#' ) IS NULL;
Approach 1a: Starting from Oracle 10g, Regular Expressions can be used instead of translate. This allows for more elegant filtering, e.g. including a leading sign and a decimal separator:
SELECT ROWID, mycontent FROM t1 WHERE REGEXP_LIKE( mycontent, '^[\+-]?\d+(\.\d+)?$' );
Approach 2 — Data Cleansing: Use constraint validation to write the ROWIDs of the problematic rows into a separate table “exceptions” (which can be created using Oracle’s Script “utlexcpt.sql”).
ALTER TABLE t1 ADD CONSTRAINT check_numeric CHECK ( translate( mycontent, '#0123456789','#' ) IS NULL ) ENABLE VALIDATE EXCEPTIONS INTO exceptions;
Step 2 would be to delete the problematic rows from the source table using the ROWIDs in “exceptions”.
Approach 3 — good design: After cleansing the data using one of the above approaches, either re-design your data model or change your code to use an appropriate data type. In a perfect world, this would have happened in the first place.
Thanks!!! This article saves my life!!!
LikeLike
Thanks to let me know it helped, 茶树!
LikeLike
Here is the quick method to find.
SELECT *
from
where instr(, chr(13)) > 0;
LikeLike
Hello Asif,
“the quick method to find” … what exactly? And where (there are missing parts in your SQL)?
LikeLike
Oracle error 1722: java.sql.SQLSyntaxErrorException: ORA-01722: invalid number has been detected in ValueSet.isIdValid
LikeLike
Great post! I spent a day and nothing made much sense on why I was getting that error in the first place. It did not cause any trouble while using an aggregate function without grouping in the query, but after using the ‘GROUP BY’ clause it threw the exception. Thank you so much!
LikeLike
Nice to know it helped. Thanks for your feedback!
LikeLike