web-dev-qa-db-ja.com

「安全」TO_NUMBER()

SELECT TO_NUMBER('*') FROM DUAL

これは明らかに例外を私に与えます:

ORA-01722:番号が無効です

それを「スキップ」して、代わりに0またはNULLを取得する方法はありますか?

全体の問題:私はNVARCHAR2フィールドを持っていますが、これには数字が含まれ、ほとんど;-)がありません(*など)、列から最大の数字を選択する必要があります。

はい、私はそれがひどいデザインであることを知っていますが、これは私が今必要なものです...:-S

[〜#〜] upd [〜#〜]

私は自分でこの問題を解決しました

COALESCE(TO_NUMBER(REGEXP_SUBSTR(field, '^\d+')), 0)
50
zerkms

私はこれ以上良いものを見つけることができませんでした:

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;
15
Gabe
COALESCE(TO_NUMBER(REGEXP_SUBSTR(field, '^\d+(\.\d+)?')), 0) 

また、スケール> 0(小数点の右側の数字)の数値を取得します。

26
pweitzman

Oracle Database 12c Release 2から、TO_NUMBERDEFAULT ... 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;

db <> fiddle demo

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

12312に変換しますが、123aまたは12a3to .

10
sOliver

元の質問とかなり古い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
)
7
stjohnroe

おそらく、独自の正規表現を使って数値をテストするのは少し面倒ですが、以下のコードは機能する可能性があります。組み込みのOracle機能を使用しているため、ユーザー定義関数を含むGabeによる他のソリューションはより堅牢であると思います(そして私の正規表現はおそらく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
1
Mike Meyers
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などの文字列を除外します。また、ここでは正規表現を使用していません。

1
usbo

最善の方法が機能ソリューションのようですが、もしあなたが苦労している環境で必要な特権を持っていなければ(私のように)、これを試すことができます:

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

ところで:私の場合、問題は「、」と「。」が原因でした。 :)それを考慮に入れてください。 this one からインスピレーションを受けています。また、 これ はより簡潔に見えます。

ちなみに:親愛なるオラクル、このような小さいが非常に貴重なニーズのために、いくつかの組み込み関数を作成してください。

0
Gultekin

以前のソリューション(@sOliverと@Mike Meyersから)の組み合わせと、REGEXPから最後の「$」を削除して、できるだけ多くの数字を取得しようとします。

構成テーブルから実際の番号をフィルタリングするために使用でき、「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                                      
0
Victor H