“安全”TO_NUMBER()

时间:2010-12-20 04:48:54

标签: oracle type-conversion ora-01722

SELECT TO_NUMBER('*') FROM DUAL

这显然给了我一个例外:

  

ORA-01722:无效号码

有没有办法“跳过”它而取而代之的是0NULL

整个问题:我有NVARCHAR2字段,其中包含数字而不是几乎;-)(如*),我需要从列中选择最大数字。

是的,我知道这是一个糟糕的设计,但这就是我现在需要的......:-S

UPD

对于我自己,我用

解决了这个问题
COALESCE(TO_NUMBER(REGEXP_SUBSTR(field, '^\d+')), 0)

9 个答案:

答案 0 :(得分:24)

COALESCE(TO_NUMBER(REGEXP_SUBSTR(field, '^\d+(\.\d+)?')), 0) 

还会获得带有比例>的数字0(小数点右边的数字)。

答案 1 :(得分:14)

我找不到比这更好的东西:

function safe_to_number(p varchar2) return number is
    v number;
  begin
    v := to_number(p);
    return v;
  exception when others then return 0;
end;

答案 2 :(得分:10)

ListView开始,您可以将 TO_NUMBER Oracle Database 12c Release 2一起使用:

DEFAULT ... ON CONVERSION ERROR

SELECT TO_NUMBER('*' DEFAULT 0 ON CONVERSION ERROR) AS "Value" FROM DUAL;

CAST

<强> db<>fiddle demo

答案 3 :(得分:8)

select COALESCE(TO_NUMBER(REGEXP_SUBSTR( field, '^(-|+)?\d+(\.|,)?(\d+)?$')), 0) from dual;

它会将 123 转换为 123 ,但 123a 12a3 转换为 0

答案 4 :(得分:7)

拟合原始问题而不是旧skool

select a, decode(trim(translate(b,'0123456789.',' ')),null,to_number(b),0)  from 
(
    select '1' a, 'not a number' b from dual
    union
    select '2' a, '1234' b from dual
)

答案 5 :(得分:1)

滚动你自己的正则表达式以测试一个数字可能有点乱,但下面的代码可能有效。我认为Gabe涉及用户定义函数的另一个解决方案更加健壮,因为您使用的是内置的Oracle功能(并且我的正则表达式可能不是100%正确),但它可能值得一试:

with my_sample_data as (
  select '12345' as mynum from dual union all
  select '54-3' as mynum from dual union all
  select '123.4567' as mynum from dual union all
  select '.34567' as mynum from dual union all
  select '-0.3462' as mynum from dual union all
  select '0.34.62' as mynum from dual union all
  select '1243.64' as mynum from dual 
)
select 
  mynum, 
  case when regexp_like(mynum, '^-?\d+(\.\d+)?$') 
    then to_number(mynum) end as is_num
from my_sample_data

然后将提供以下输出:

MYNUM   IS_NUM
-------- ----------
12345   12345
54-3    
123.4567    123.4567
.34567  
-0.3462 -0.3462
0.34.62 
1243.64 1243.64

答案 6 :(得分:1)

select DECODE(trim(TRANSLATE(replace(replace(A, ' '), ',', '.'), '0123456789.-', ' ')),
              null,
              DECODE(INSTR(replace(replace(A, ' '), ',', '.'), '.', INSTR(replace(replace(A, ' '), ',', '.'), '.') + 1),
                     0,
                     DECODE(INSTR(replace(replace(A, ' '), ',', '.'), '-', 2),
                            0,
                            TO_NUMBER(replace(replace(A, ' '), ',', '.'))))) A
  from (select '-1.1' A from DUAL union all select '-1-1' A from DUAL union all select ',1' A from DUAL union all select '1..1' A from DUAL) A;

此代码排除了以下字符串:-1-1,1..1,12-2等。而且我还没有在这里使用正则表达式。

答案 7 :(得分:0)

以前的解决方案(来自@sOliver和@Mike Meyers)的组合,并试图通过移除最后一个&#39; $&#39;来尽可能多地抓取数字。来自REGEXP。

它可用于从配置表中过滤实际数字,并具有&#34;种类的&#34;数字旁边的评论为&#39; 12天&#39;。

with my_sample_data as ( select '12345' as mynum from dual union all select '123.4567' as mynum from dual union all select '-0.3462' as mynum from dual union all select '.34567' as mynum from dual union all select '-.1234' as mynum from dual union all select '**' as mynum from dual union all select '0.34.62' as mynum from dual union all select '24Days' as mynum from dual union all select '42ab' as mynum from dual union all select '54-3' as mynum from dual ) SELECT mynum, COALESCE( TO_NUMBER( REGEXP_SUBSTR( mynum, '^(-|+)?\d*(.|,)?(\d+)?') ) , 0) is_num FROM my_sample_data;

会给出


MYNUM    IS_NUM                                  
-------- ----------
12345    12345                                   
123.4567 123.4567                                
-0.3462  -0.3462                                 
.34567   0.34567                                 
-.1234   -0.1234                                 
**       0                                       
0.34.62  0.34                                    
24Days   24                                      
42ab     42                                      
54-3     54                                      

答案 8 :(得分:0)

最好的方法似乎是功能解决方案,但如果你在环境中没有必要的权限(比如我),那么你可以试试这个:

SELECT
 CASE
  WHEN
     INSTR(TRANSLATE('123O0',
                     ' qwertyuıopğüasdfghjklşizxcvbnmöçQWERTYUIOPĞÜASDFGHJKLŞİZXCVBNMÖÇ~*\/(){}&%^#$<>;@€|:_=',
                     'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
                     ),
       'X') > 0
  THEN 'Y'
  ELSE 'N'
END is_nonnumeric
FROM DUAL

顺便说一句:在我的情况下,问题是由于&#34;,&#34;和&#34;。&#34; :)所以考虑一下。灵感来自this one。同样this one似乎更简洁。

顺便说一句2:亲爱的Oracle,您能否为这么小但非常宝贵的需求创建一些内置函数?