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
/
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
/