提问人:zerkms 提问时间:12/20/2010 最后编辑:Lukasz Szozdazerkms 更新时间:10/23/2018 访问量:83588
“安全” TO_NUMBER()
"Safe" TO_NUMBER()
问:
SELECT TO_NUMBER('*') FROM DUAL
这显然给了我一个例外:
ORA-01722:编号无效
有没有办法“跳过”它并得到或代替?0
NULL
整个问题:我有字段,其中包含数字而不是几乎 ;-)(比如),我需要从列中选择最大的数字。NVARCHAR2
*
是的,我知道这是一个糟糕的设计,但这就是我现在需要的...... :-S
更新:
就我自己而言,我已经解决了这个问题
COALESCE(TO_NUMBER(REGEXP_SUBSTR(field, '^\d+')), 0)
答:
我找不到比这更好的东西了:
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;
评论
54-3
54
exception when invalid_number then return 0;
滚动自己的正则表达式来测试数字可能有点混乱,但下面的代码可能有效。我认为 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
适合原来的问题和相当古老的 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
)
评论
543
54-3
54
COALESCE(TO_NUMBER(REGEXP_SUBSTR(field, '^\d+(\.\d+)?')), 0)
还将获得小数位数为 0 >(小数点右侧的数字)的数字。
评论
^\-?\d*\.?\d*$
.
^\d+(\.\d+)?
1
field
1x
0' if
0.0.1
0
field
0 6
select COALESCE(TO_NUMBER(REGEXP_SUBSTR( field, '^(-|+)?\d+(\.|,)?(\d+)?$')), 0) from dual;
它会将 123 转换为 123,但将 123a 或 12a3 转换为 0。
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 等。而且我在这里没有使用正则表达式。
从您可以将TO_NUMBER与:Oracle Database 12c Release 2
DEFAULT ... ON CONVERSION ERROR
SELECT TO_NUMBER('*' DEFAULT 0 ON CONVERSION ERROR) AS "Value"
FROM DUAL;
或:CAST
SELECT CAST('*' AS NUMBER DEFAULT 0 ON CONVERSION ERROR) AS "Value"
FROM DUAL;
评论
结合以前的解决方案(来自 @sOliver 和 @Mike Meyers),并试图通过从 REGEXP 中删除最后一个“$”来获取尽可能多的数字。
它可用于从配置表中筛选实际数字,并在数字旁边有一个“kind-of”注释为“12 天”。
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
最好的方法似乎是功能解决方案,但如果你在你正在挣扎的环境中没有必要的权限(像我一样),那么你可以试试这个:
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
顺便说一句:就我而言,问题是由于“,”和“.”:)所以要考虑到这一点。灵感来自这个。此外,这个似乎更简洁。
顺便说一句 2:亲爱的 Oracle,您能为如此小但宝贵的需求创建一些内置函数吗?
评论
REGEXP_SUBSTR(field, '^\d+')