这会将十进制数字转换为罗马数字:
select to_char(515, 'RN') from dual;
返回值: DXV
怎么做相反呢?抛出ORA-01722: Invalid number
:
select to_number('DXV', 'RN') from dual;
只是为了好玩,另一种方法是将字符串分成单个和相邻数字组,允许使用标准减法表示法(谢谢,维基百科;将每个单个或相邻对或数字转换为它们的十进制等效项;然后将它们求和:
with t (str) as (select 'MCMLXXXIV' from dual)
select sum(
case regexp_substr(str, '(CM|M|CD|D|XC|C|XL|L|IX|X|IV|V|I)', 1, level)
when 'M' then 1000
when 'CM' then 900
when 'D' then 500
when 'CD' then 400
when 'C' then 100
when 'XC' then 90
when 'L' then 50
when 'XL' then 40
when 'X' then 10
when 'IX' then 9
when 'V' then 5
when 'IV' then 4
when 'I' then 1
end) as decimals
from t
connect by regexp_substr(str, '(CM|M|CD|D|XC|C|XL|L|IX|X|IV|V|I)', 1, level) is not null;
DECIMALS
----------
1984
请注意,正则表达式中搜索项的顺序与其等效的十进制顺序不同;您需要将M之前的CM匹配为减号。
如果您需要做很多事情,那么可能值得创建一个确定性函数(当然,对于递归CTE方法也是如此)。在这种情况下,您可以切换到PL / SQL循环以减少上下文切换:
create or replace function roman_to_decimal(p_roman varchar2)
return number deterministic is
l_decimal number := 0;
begin
for i in 1..regexp_count(p_roman, '(CM|M|CD|D|XC|C|XL|L|IX|X|IV|V|I)') loop
l_decimal := l_decimal +
case regexp_substr(p_roman, '(CM|M|CD|D|XC|C|XL|L|IX|X|IV|V|I)', 1, i)
when 'M' then 1000
when 'CM' then 900
when 'D' then 500
when 'CD' then 400
when 'C' then 100
when 'XC' then 90
when 'L' then 50
when 'XL' then 40
when 'X' then 10
when 'IX' then 9
when 'V' then 5
when 'IV' then 4
when 'I' then 1
end;
end loop;
return l_decimal;
end;
/
select roman_to_decimal('DXV'), roman_to_decimal('MCMLXXXIV')
from dual;
ROMAN_TO_DECIMAL('DXV') ROMAN_TO_DECIMAL('MCMLXXXIV')
----------------------- -----------------------------
515 1984
您可以通过以下方式查看并检查所有转化(1-3999,因为这是所支持的范围to_char()
):
with t (orig, roman) as (
select level, to_char(level, 'RN') from dual connect by level < 4000
)
select orig, roman, roman_to_decimal(roman)
from t;
ORIG ROMAN ROMAN_TO_DECIMAL(ROMAN)
---------- --------------- -----------------------
1 I 1
2 II 2
3 III 3
4 IV 4
5 V 5
6 VI 6
7 VII 7
8 VIII 8
9 IX 9
10 X 10
11 XI 11
...
3994 MMMCMXCIV 3994
3995 MMMCMXCV 3995
3996 MMMCMXCVI 3996
3997 MMMCMXCVII 3997
3998 MMMCMXCVIII 3998
3999 MMMCMXCIX 3999
或只是为了验证它们都转换回原始值:
with t (original, roman) as (
select level, to_char(level, 'RN') from dual connect by level < 4000
)
select original, roman, roman_to_decimal(roman)
from t
where roman_to_decimal(roman) != original;
no rows selected
对于我来说,这比递归CTE慢得多,但在其他版本和平台上可能有所不同。就像我说的,只是为了好玩...
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句