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

bar*_*art 11 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...
Run Code Online (Sandbox Code Playgroud)

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...
Run Code Online (Sandbox Code Playgroud)

还有另一种使用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;
Run Code Online (Sandbox Code Playgroud)

正是我想要的,但如果你在查询中多次这样做,它似乎效率不高.您无法缓存v_decimal的值(获取一次并存储在包变量中),因为它不知道您是否更改了NLS_NUMERIC_CHARACTERS的会话值,然后它会再次中断.

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

Vin*_*rat 11

以下应该有效:

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

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

有趣的是,如果你有一个非常大的字符串,那么简单to_number(:x)就可以了,而这种方法会失败.

编辑:感谢sOliver支持负数.

  • 完成 _to_number_ 函数的“fmt”参数可能很有用,如下所示:“translate(:x, '0123456789.,-+', '9999999999DG')”。这会停止负数上的 ORA-01481 错误。 (2认同)