Oracle PL/SQL - 检查数字格式和掩码

Oracle PL/SQL - Check number formats and masks

很久没做PL/SQL... 我需要来自社区的建议,对于一些显然微不足道的事情,但我有点坚持这一点。

我正在从 CSV 文件构建负载,其中有一列包含金额。 CSV 来自不同的供应商,每个供应商都可以以不同的格式发送金额。 因此,我应该拒绝 CSV 中金额格式不正确的行 (999,999,999,999.00),因为供应商报告的金额可能不正确,应该予以修复。

999.999.999,00 或 999999999,99 格式,我可以通过 PL/SQL 做一些处理来转换。 但是我在使用不同格式的值时遇到问题,例如 999,9,9,9 或其他...

我正在尝试使用常用函数(TO_NUMBER、TO_CHAR)。但是没有太大的成功...

  1. SELECT TO_NUMBER('999,9,9,9') FROM DUAL;SELECT TO_NUMBER('999,9,9,9','99G990G990G990G990G990D00') FROM DUAL;

结果是 ORA-01722:数字无效,太棒了!但是,它会拒绝其他看起来正确的格式,例如 9,999.99

  1. SELECT TO_NUMBER('999,9,9,9','99G999G999G999G999G999D00') FROM DUAL;

使用格式掩码,值 999,9,9,9 被转换为 999999 - 但不正确。但是,例如,使用格式掩码可以很好地处理 9,999.99。

您是否知道 Oracle 提供的任何其他功能可以帮助解决我的问题?

或者关于如何执行此操作的任何建议?

非常感谢。

附件,

吉列尔梅

您可以使用正则表达式来确定数字是否有效。然后,如果有效,用空值替换逗号,然后将其转换为数字。下面的查询使用以下正则表达式:

'^(\d{1,3})(\,\d{3})*(\.\d{2}|\.?)$'

细分如下

  • ^(\d{1,3}) --- 字符串开头1到3位数字
  • (\,\d{3})* --- 后跟 0 组或多组逗号后跟 3 位数字
  • (.\d{2}|.?) --- 后跟小数点后跟 2 位数字或 (|) 可选小数点
  • $ --- 字符串结尾


演示:

with test (num, expected)
   as (select '999,999,999,999.00', 'valid'    from dual union all
       select '999,99,999,999.00',  'invalid'  from dual union all 
       select '999.00',             'valid'    from dual union all 
       select '99',                 'valid'    from dual union all 
       select '9,999.',             'valid'    from dual union all 
       select '9,999..0',           'invalid'  from dual union all         
       select '999,99999,999.00',   'invalid'  from dual  
      )
select num
     , expected
     , case when regexp_like(num,'^(\d{1,3})(\,\d{3})*(\.\d{2}|\.?)$')   
            then to_char(to_number(replace(num,',',null)))
            else 'Not Valid Number'
       end converted
  from test;

在现场设置中,您不需要 "to_char(to_number ..." 结构)。这用于 demonstration/testing 因为 case 语句的 then 和 else 必须产生相同的数据类型。实时版本会显示为:

with test (num)
   as (select '999,999,999,999.00'  from dual union all
       select '999,99,999,999.00'   from dual union all 
       select '999.00'              from dual union all 
       select '99'                  from dual union all 
       select '9,999.'              from dual union all 
       select '9,999..0'            from dual union all         
       select '999,99999,999.00'    from dual  
      )
select to_number(replace(num, ',', null))
  from test
 where regexp_like(num,'^(\d{1,3})(\,\d{3})*(\.\d{2}|\.?)$');