Oracle国产化改造迁移时的问题: Number data type中的 invalid number
当迁移Oracle数据库到其他数据库系统时,你可能会遇到一个棘手的问题:在Oracle表中存在无效的数字数据,导致在目标数据库(比如PostgreSQL系列)导入时报错。这些错误可能表现为类似“invalid input syntax for type numeric: ‘xxx’”的提示信息。最近,在将Oracle国产化改造项目迁移到商业发行的MogDB时,我也遇到了这个问题。看来Oracle的容错率太高了,不会像其他数据库那样严格检查数据的有效性。因此,我们需要一种方法来找出并修正这些错误数据。在本文中,我将分享一种解决这个问题的方法。
MTK工具入库错误
code: 22p02 msg: invalid input syntax for type numeric:“0.00000023H4” where: copy TAB123, column COL123: “0.00000023H4”
data: 1 xxx xx xx 0.00000023H4 …
MTK( Database Migration Toolkit)工具是导入Mogdb的异构数据迁移工具,错误提示很明确TAB123表COL123字段的”0.00000023H4″值是无效数字,并提示了该行记录,接下来我们去源库确认。
Oracle源库确认数据
— 注 环境oracle 19c ,报错数据列导出到临时库演示。
Connected to: Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production Version 19.3.0.0.0 SQL> @init SQL> @desc test1 Name Null? Type ------------------------------- -------- ---------------------------- 1 ID NUMBER(38) 2 V NUMBER SQL> select * from test1; ID V ---------- ---------- 1 1 2 3 SQL> col dv for a40 SQL> select id,v,dump(v,1016) dv from test1; ID V DV ---------- ---------- ---------------------------------------- 1 1 Typ=2 Len=2: c1,2 2 NULL 3 Typ=2 Len=3: bd,18,f5
Note:
ID=3对应的V列就是报错的数据,值不可见并且不是NULL。
ORA-01722错误
SQL> select length(v) from test1; ERROR: ORA-01722: invalid number no rows selected SQL> select to_number(v) from test1 where id=1; TO_NUMBER(V) ------------ 1 SQL> select to_number(v) from test1 where id=2; TO_NUMBER(V) ------------ SQL> select to_number(v) from test1 where id=3; select to_number(v) from test1 where id=3 * ERROR at line 1: ORA-01722: invalid number SQL> select to_char(v) from test1 where id=3; select to_char(v) from test1 where id=3 * ERROR at line 1: ORA-01722: invalid number
Note:
常规函数提示ORA-01722: invalid number,显然是个无效数字。 这类通常是修正,或者制空修改为null就可以,但首选是可以快速从几千万或上亿的表中找到该数据。
如何查找无效的NUMVER数据?
对于字符类型的字段
对于数据库中是char或varchar字符型的值,转number报错的方法较多,如基于exception创建的自定义函数,或Oracle 12c(12.2)to_xxx 函数或cast 函数的增强, 对于开发人员实在喜欢。
# 自定义函数 CREATE OR REPLACE FUNCTION IS_NUMERIC(P_INPUT IN VARCHAR2) RETURN INTEGER IS RESULT INTEGER; NUM NUMBER ; BEGIN NUM:=TO_NUMBER(P_INPUT); RETURN 1; EXCEPTION WHEN OTHERS THEN RETURN 0; END IS_NUMERIC; / -- or -- CREATE OR REPLACE FUNCTION IS_NUMERIC2(P_INPUT IN VARCHAR2) RETURN number IS NUM NUMBER ; BEGIN NUM:=TO_NUMBER(P_INPUT); RETURN 1; EXCEPTION WHEN value_error THEN RETURN 0; END; / SQL> create table test2(id int,v varchar2(20)); Table created. SQL> insert into test2 values(1,1000); 1 row created. SQL> insert into test2 values(2,200.1); 1 row created. SQL> insert into test2 values(3,'99%'); 1 row created. SQL>insert into test2 values(4,'$123'); 1 row created. SQL> select * from test2; ID V ---------- ---------------------------------------- 1 1000 2 200.1 3 99% 4 $123 SQL> select id,v,is_numeric(v) from test2; ID V IS_NUMERIC(V) ---------- ---------------------------------------- ------------- 1 1000 1 2 200.1 1 3 99% 0 4 $123 0 SQL> select id,v from test2 where IS_NUMERIC(V)=0; ID V ---------- ---------------------------------------- 3 99% 4 $123 SQL> select id,v from test2 where IS_NUMERIC2(V)=0; ID V ---------- ---------------------------------------- 3 99% 4 $123 # TO_* Conversion Functions SQL> select id,v,to_number(v) from test2; ERROR: ORA-01722: invalid number SQL> select id,v,to_number(v default -99999999 on conversion error) from test2; ID V TO_NUMBER(VDEFAULT-99999999ONCONVERSIONERROR) ---------- ---------------------------------------- --------------------------------------------- 1 1000 1000 2 200.1 200.1 3 99% -99999999 4 $123 -99999999 SQL> select id,v from test2 where to_number(v default -99999999 on conversion error)=-99999999; ID V ---------- ---------------------------------------- 3 99% 4 $123 # CAST Conversion Functions SQL> select id, v,cast(v as number) from test2; ERROR: ORA-01722: invalid number no rows selected SQL> select id, v,cast(v as number) from test2 where id=1; ID V CAST(VASNUMBER) ---------- ---------------------------------------- --------------- 1 1000 1000 SQL> select id, v,cast(v as number 2 default -99999999 on conversion error) from test2; ID V CAST(VASNUMBERDEFAULT-99999999ONCONVERSIONERROR) ---------- ---------------------------------------- ------------------------------------------------ 1 1000 1000 2 200.1 200.1 3 99% -99999999 4 $123 -99999999
Note:
从oracle 12c r2不用创建自定义函数也可以做类型转换报错的捕捉,CAST函数 (以及TO_NUMBER、TO_BINARY_FLOAT、TO_BINARY_DOUBLE、TO_DATE、TO_TIMESTAMP、TO_TIMESTAMP_TZ、TO_DSINTERVAL和 T O_YMINTERVAL函数)现在可以返回用户指定的值,而不是错误,增加了DEFAULT XXX ON CONVERSION ERROR语法,当然还有另一个函数在此版本引入,我会在下面介绍,对于本案例已经是number数据类型的列类型可以使用吗?下面继续测试test1的的数据“对于number类型的字段”
函数官方文档了解更多
- CAST
- TO_BINARY_DOUBLE
- TO_BINARY_FLOAT
- TO_DATE
- TO_DSINTERVAL
- TO_NUMBER
- TO_TIMESTAMP
- TO_TIMESTAMP_TZ
- TO_YMINTERVAL
- VALIDATE_CONVERSION
对于number类型的字段
SQL> select id,v,IS_NUMERIC(v),IS_NUMERIC2(v) from test1; ERROR: ORA-01722: invalid number SQL> select id,v,to_number(v default -99999999 on conversion error) from test1; ERROR: ORA-01722: invalid number no rows selected SQL> select id,v,cast(v as number default -99999999 on conversion error) from test1; ERROR: ORA-01722: invalid number no rows selected
Note:
对于已经是number类型的列使用以上函数就无法匹配了,还是直接会报ORA-01722: invalid number, 感谢@FranckPachot的提示使用VALIDATE_CONVERSION function.
VALIDATE_CONVERSION函数
12c R2新的 VALIDATE_CONVERSION() 函数可用于帮助您识别无法转换为所需数据类型的列值.如果转换成功返回1,否则返回0。我们继续使用上面的test1和test2测试数据演示。
# 对于char类型的字段 SQL> SELECT ID,V,validate_conversion(V AS NUMBER),validate_conversion(V AS NUMBER,'$99999') from test2; ID V VALIDATE_CONVERSION(VASNUMBER) VALIDATE_CONVERSION(VASNUMBER,'$99999') ---------- ---------------------------------------- ------------------------------ --------------------------------------- 1 1000 1 0 2 200.1 1 0 3 99% 0 0 4 $123 0 1 # 对于number类型的字段 SQL> select id,v,dump(v) dv, validate_conversion(V AS NUMBER) is_valid_number from test1; ID V DV IS_VALID_NUMBER ---------- ---------- ---------------------------------------- --------------- 1 1 Typ=2 Len=2: 193,2 1 2 NULL 0 3 Typ=2 Len=3: 189,24,245 0 SQL> select id,v,dump(v) dv, validate_conversion(V AS NUMBER) is_valid_number from test1 where v is not null and validate_conversion(V AS NUMBER)=0; ID V DV IS_VALID_NUMBER ---------- ---------- ---------------------------------------- --------------- 3 Typ=2 Len=3: 189,24,245 0
Note:
validate_conversion是oracle 12.2SQL方面的新功能,在数据类型转换判断上是个非常有用的工具,可以简单判断逻辑,可见他比cast和to_*函数验证的阶段更早,对于本案例中的已是number类型的列中的值,依旧可以判断值的有效性,支持的数据类型data type conversions有Number.Date.Timestamp.Timestamp with time zone.Binary_float.Binary_double.Interval day to second.Interval year to month.
— enjoy —
对不起,这篇文章暂时关闭评论。