【问题标题】:PLSQL SUBSTR function ignore the trailing zeroPLSQL SUBSTR 函数忽略尾随零
【发布时间】:2020-08-21 03:56:02
【问题描述】:
select TO_NUMBER (SUBSTR(10.31, INSTR (10.31, '.') + 1)) from dual

以上查询返回31 作为输出。但下面的查询返回3 作为输出。

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

我怎样才能得到30 而不是3 作为输出?

【问题讨论】:

  • @WilliamRobertson "Oracle Database 18c Express Edition Release 18.0.0.0.0 - Production Version 18.4.0.0.0"

标签: plsql substr


【解决方案1】:

从 cmets 看来,您从一个想要转换为单词的数值开始,您应该首先将其拆分为美元和美分。

如果你真的需要使用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' 的儒略表示),您可以从 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
/

【讨论】:

  • 感谢您的回答。看起来第一个解决方法正在回答我的问题。我会检查并通知您。
  • 对于大于 5373484 的数字的任何解决方案?因为julian date must be between 1 and 5373484。 number 大于 5373484 时查询失败。
【解决方案2】:

实际上,我很惊讶您当前的查询甚至运行没有错误,因为 Oracle 的 SUBSTR 函数应该对 字符串 而不是数字进行操作。话虽这么说,如果您正确地使用带有字符串的当前查询,那么它可以工作:

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;

这将只保留出现在小数点之后的数字,以防出现小数点。否则,它只会返回没有小数部分的输入的所有数字。

【讨论】:

  • "我真的很惊讶您当前的查询甚至没有错误地运行" 这里也一样。错误地忘记用引号将数字括起来,Oracle 为我进行了类型转换。感谢您的回答。
  • @Bishan 顺便说一句,如果您 正在 真的在处理数字,那么您可能想要这样的东西:SELECT 10.31 - FLOOR(10.31) AS output FROM dual ...也就是说,如果您只想捕获数字的小数部分。
  • 实际上我正在尝试用文字显示数字值。 (比如十和三十美分,十和五美分等)。目前我正在使用这个带有给定数字(TO_CHAR (TO_DATE (TO_NUMBER (SUBSTR(inputNum, INSTR (inputNum, '.') + 1)),'J'),'JSP'))的小数部分的命令,这给了我THREE10.0310.30
  • 我们可以解决格式问题,但是不使用零值可以吗?您的 Oracle 版本是多少?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-10-21
  • 1970-01-01
  • 1970-01-01
  • 2017-02-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多