如何在Oracle PL/SQL中使用非默认NLS\u数字字符高效地将文本转换为数字?

如何在Oracle PL/SQL中使用非默认NLS\u数字字符高效地将文本转换为数字?,oracle,plsql,Oracle,Plsql,我正试图找到一种在PL/SQL中从字符串转换为数字的有效、通用的方法,其中NLS\u NUMERIC\u CHARACTERS设置的本地设置是可预测的——最好不要碰它。输入格式为编程标准“123.456789”,但小数点两侧的位数未知 select to_number('123.456789') from dual; -- only works if nls_numeric_characters is '.,' select to_number('123.456789', '99999.9

我正试图找到一种在PL/SQL中从字符串转换为数字的有效、通用的方法,其中NLS\u NUMERIC\u CHARACTERS设置的本地设置是可预测的——最好不要碰它。输入格式为编程标准“123.456789”,但小数点两侧的位数未知

select to_number('123.456789') from dual;
  -- only works if nls_numeric_characters is '.,'

select to_number('123.456789', '99999.9999999999') from dual;
  -- only works if the number of digits in the format is large enough
  -- but I don't want to guess...
to_number
接受第三个参数,但在这种情况下,您也需要指定第二个参数,并且“default”没有格式规范

使用PL/SQL还有另一种方法:

CREATE OR REPLACE
FUNCTION STRING2NUMBER (p_string varchar2) RETURN NUMBER
IS
  v_decimal char;
BEGIN
  SELECT substr(VALUE, 1, 1)
  INTO v_decimal
  FROM NLS_SESSION_PARAMETERS
  WHERE PARAMETER = 'NLS_NUMERIC_CHARACTERS';
  return to_number(replace(p_string, '.', v_decimal));
END;
/

select string2number('123.456789') from dual;
这正是我想要的,但如果你在一个查询中多次这样做,它似乎没有效率。无法缓存v_decimal的值(提取一次并存储在包变量中),因为它不知道是否更改NLS_NUMERIC_字符的会话值,然后它会再次中断


我是不是忽略了什么?还是我担心得太多了,Oracle在这方面的效率比我认为的要高得多?

以下几点应该有效:

SELECT to_number(:x, 
                 translate(:x, '012345678-+', '999999999SS'), 
                 'nls_numeric_characters=''.,''') 
  FROM dual;
它将使用高效的
translate
生成正确的第二个参数
999.9999999
,因此您不必事先知道有多少位数字。它将适用于所有受支持的Oracle数字格式(在10.2.0.3中最多62位有效数字)

有趣的是,如果您有一个非常大的字符串,那么简单的
to_number(:x)
将起作用,而此方法将失败


编辑:由于支持负数。

如果您每个会话都要做大量工作,可以选择使用 ALTER会话集NLS_数值_字符=',' 在任务开始时

当然,如果在同一个会话中执行了大量其他代码,您可能会得到奇怪的结果:-) 但是,我们可以在数据加载过程中使用此方法,因为我们有专门的程序,它们有自己的连接池用于加载数据。

对不起,我后来注意到您的问题是另一种情况。然而,值得注意的是,对于相反的方向,有一个简单的解决方案:

有点晚了,但今天我注意到特殊的格式掩码“TM9”和“TME”,它们被描述为“文本最小数格式模型返回(十进制输出)尽可能少的字符数。”

TM9的发明似乎就是为了解决这个特殊问题:

select to_char(1234.5678, 'TM9', 'NLS_NUMERIC_CHARACTERS=''.,''') from dual;
结果是
'1234.5678'
没有前导空格或尾随空格,并且有一个小数点,尽管我的环境包含
NLS_LANG=GERMAN_GERMAN.WE8MSWIN1252
,这通常会导致小数点逗号

select to_number(replace(:X,'.',to_char(0,'fmd'))) from dual;
顺便说一句

如果你想更严格些

select to_number(translate(:X,to_char(0,'fmd')||'.','.'||to_char(0,'fmd'))) from dual;

完成to_number函数的'fmt'参数可能有用,如下所示:
translate(:x,'0123456789.,-+','999999DG')
。这将阻止ORA-01481在负数上出错。更难的示例是转换
'12.34E5'
。我找不到可以接受的格式,但是单个参数
to_number
在解析它时没有问题。@jammydowger它确实有效,并且回答了问题。很高兴得到-1分。我请求你。凉爽的
select to_number(replace('1.2345e-6','.',to_char(0,'fmd'))) from dual;
select to_number(translate(:X,to_char(0,'fmd')||'.','.'||to_char(0,'fmd'))) from dual;