如何使用非默认NLS_NUMERIC_CHARACTERS在Oracle PL / SQL中有效地将文本转换为数字?

时间:2010-11-10 09:22:56

标签: oracle plsql

我正在尝试找到一种有效的通用方法,将字符串转换为PL / SQL中的数字,其中NLS_NUMERIC_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接受第三个参数,但在这种情况下,您也要指定第二个参数,并且“默认”没有格式规范......

select to_number('123.456789', null, 'nls_numeric_characters=''.,''') from dual;
  -- returns null

select to_number('123.456789', '99999D9999999999', 'nls_numeric_characters=''.,''') from dual;
  -- "works" with the same caveat as (2), so it's rather pointless...

还有另一种使用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_CHARACTERS的会话值,然后它会再次中断。

我忽略了什么吗?或者我担心的太多了,而且甲骨文这样做的效率要高得多,那么我会把它归功于?

5 个答案:

答案 0 :(得分:11)

以下内容应该有效:

SELECT to_number(:x, 
                 translate(:x, '012345678-+', '999999999SS'), 
                 'nls_numeric_characters=''.,''') 
  FROM dual;

它将使用有效的999.999999构建正确的第二个参数translate,因此您不必事先知道有多少位数。它将与所有支持的Oracle数字格式一起使用(显然在10.2.0.3中最多62位有效数字)。

有趣的是,如果你有一个非常大的字符串,简单的to_number(:x)将起作用,而这种方法将失败。

编辑:感谢sOliver支持否定数字。

答案 1 :(得分:4)

如果您在每个会话中做了大量工作,可能会使用一个选项 ALTER SESSION SET NLS_NUMERIC_CHARACTERS ='。,' 在任务开始时。

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

答案 2 :(得分:3)

很抱歉,我后来注意到你的问题反过来了。然而值得注意的是,对于相反的方向,有一个简单的解决方案:

有点晚了,但今天我注意到特殊格式掩码'TM9'和'TME'被描述为“文本最小数字格式模型返回(十进制输出)可能的最小字符数。”在https://docs.oracle.com/cloud/latest/db112/SQLRF/sql_elements004.htm#SQLRF00210

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

select to_char(1234.5678, 'TM9', 'NLS_NUMERIC_CHARACTERS=''.,''') from dual;

结果是'1234.5678'没有前导或尾随空白,并且尽管我的环境包含NLS_LANG=GERMAN_GERMANY.WE8MSWIN1252,但是小数点,这通常会导致小数COMMA。

答案 3 :(得分:0)

数字的数量是无限的现实吗? 如果我们假设是这样,那么更仔细地研究需求难道不是一个很好的理由吗?

如果我们在初始字符串超长时遇到这种奇妙的情况,那么下面的方法就行了:

select 
  to_number(
    '11111111.2222'
  , 'FM' || lpad('9', 32, '9') || 'D' || lpad('9', 30, '9')
  , 'NLS_NUMERIC_CHARACTERS=''.,'''
  ) 
from 
  dual

答案 4 :(得分:-1)

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

btw

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;