PLSQL SUBSTR 函数忽略尾随零

PLSQL SUBSTR function ignore the trailing zero

select TO_NUMBER (SUBSTR(10.31, INSTR (10.31, '.') + 1)) from dual

以上查询 returns 31 作为输出。但是下面查询 returns 3 作为输出。

select TO_NUMBER (SUBSTR(10.30, INSTR (10.30, '.') + 1)) from dual

如何将 30 作为输出而不是 3

我真的很惊讶你当前的查询甚至 运行 没有错误,因为 Oracle 的 SUBSTR 函数应该在 strings 上运行,而不是数字。话虽这么说,如果你正确地使用你当前的字符串查询,那么它会起作用:

SELECT TO_NUMBER(SUBSTR('10.30', INSTR ('10.30', '.') + 1)) FROM dual;  -- returns 30

一种更紧凑(虽然不一定性能更高)的方法可能是使用 REGEXP_SUBSTR:

SELECT REGEXP_SUBSTR('10.30', '[0-9]+$') FROM dual;

在存在小数点的情况下,这将仅保留出现在小数点后的数字。否则,它只会 return 所有没有小数部分的输入数字。

看起来(从评论中)您是从一个要转换为单词的数值开始的,您应该首先将其拆分为美元和美分。

如果你真的需要使用substr等,那么你可以从一个已知的格式开始,比如to_char(amount,'fm9990.00'),这样它就是一个正好有两位小数的字符串。但是,如果您有数值,则使用算术函数将其转换为所需的单位会更容易。整美元是 trunc(amount),美分是 100 * mod(amount,1)

另一个问题是 'Jsp' 日期格式方法无法处理零。如果您使用的是 Oracle 12.2 或更高版本,则有一个使用 default on conversion error 子句的解决方法:

create table demo
( amount number(6,2) );

insert into demo values (10.3);
insert into demo values (.25);
insert into demo values (25);

select amount
     , nvl(to_char(to_date(trunc(amount) default null on conversion error,'J'),'Jsp'),'Zero') as dollars
     , nvl(to_char(to_date(100 * mod(amount,1) default null on conversion error,'J'),'Jsp'),'Zero') as cents
from   demo;

  AMOUNT DOLLARS      CENTS
-------- ------------ -------------
   10.30 Ten          Thirty
   25.00 Twenty-Five  Zero
    0.25 Zero         Twenty-Five

在 12.1 中,您可以使用内联函数绕过它(即使在更高版本中也可能是个不错的主意,以简化其余查询):

with
     function to_words(num number) return varchar2 as
     begin
         return
             case num
                 when 0 then 'Zero'
                 else to_char(to_date(num,'J'),'Jsp')
             end;
     end; 
select amount
     , to_words(trunc(amount)) as dollars
     , to_words(100 * mod(amount,1)) as cents
from   demo;

对于大于 5373484(日期“9999-12-31”的 Julian 表示)的值,您可以使用 Ask Tom: Spell the number 中的这个(此处转换为 WITH 子句,但您可以将其创建为独立函数):

with function spell_number
    ( p_number in number )
    return varchar2
as
    -- Tom Kyte, 2001:
    -- https://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:1407603857650
    l_num    varchar2(50) := trunc(p_number);
    l_return varchar2(4000);

    type myarray is table of varchar2(15);

    l_str myarray :=
        myarray
        ( ''
        , ' thousand '
        , ' million '
        , ' billion '
        , ' trillion '
        , ' quadrillion '
        , ' quintillion '
        , ' sextillion '
        , ' septillion '
        , ' octillion '
        , ' nonillion '
        , ' decillion '
        , ' undecillion '
        , ' duodecillion ');
begin
    for i in 1 .. l_str.count loop
        exit when l_num is null;
    
        if substr(l_num, length(l_num) -2, 3) <> 0 then
            l_return := to_char(to_date(substr(l_num, length(l_num) - 2, 3), 'J'), 'Jsp') || l_str(i) || l_return;
        end if;
        l_num := substr(l_num, 1, length(l_num) - 3);
    end loop;

    return l_return;
end spell_number;
select amount
     , spell_number(trunc(amount)) as dollars
     , spell_number(100 * mod(amount,1)) as cents
from   demo
/