考虑到数据库服务器上定义的 NLS_NUMERIC_CHARACTERS,如何使用正确的 TO_NUMBER() Oracle 函数?

How to use correclty TO_NUMBER() Oracle function considering NLS_NUMERIC_CHARACTERS defined on Database server?

今天,我发现 Oracle NLS_NUMERIC_CHARACTERS 定义不明确!

的确,从一开始,我就认为NLS_NUMERIC_CHARACTERS只是用来定义十进制数的显示方式。

但实际上,NLS_NUMERIC_CHARACTERS也在TO_NUMBER()函数中隐式使用来定义要转换的STRING数字的格式。

我的问题是我是欧洲人,对我来说,小数点分隔符实际上是逗号 (,) 而不是点 (.)。

当我显示数字时,我的偏好是使用逗号显示它。为此,NLS_NUMERIC_CHARACTERS 设置为 ',.',其中第一个字符定义数字分隔符。

到现在为止,我对此没有问题:-)

我的问题是所有 String table 的字段值中包含数字的都使用点字符作为小数点分隔符。

当我要转换任何代表数字的字符串值时,我可以使用 TO_NUMBER Oracle 函数,像这样

select TO_NUMBER(VALUE) from TB_PARAMETER;

如果小数点分隔符是点 (NLS_NUMERIC_CHARACTERS = '.,'),这会很好用,但如果小数点分隔符是逗号 (NLS_NUMERIC_CHARACTERS = ',.'),这会停止工作。

我知道我可以像这样在 TO_NUMBER() 函数中指定 NLS_NUMERIC_CHARACTERS

select TO_NUMBER(VALUE
                ,'999999999D999999999'
                ,'NLS_NUMERIC_CHARACTERS = ''.,'''
                )
  from TB_PARAMETER;

但这对我来说太冗长了!

是否有一个 Oracle 函数可以做同样的事情?

示例:

select CAST_NUMBER(VALUE) from TB_PARAMETER;

我个人认为 Oracle TO_NUMBER() 函数使用 NLS_NUMERIC_CHARACTERS 会话的参数来定义要转换的十进制字符串的格式是一个 bug .

的确,在数据库中,字符串中使用的小数点分隔符总是固定的。它是一个逗号或一个点,但从来没有,一次是逗号,另一次是点。

如果我的分析是正确的,TO_NUMBER() 函数必须始终使用不对应(在所有情况下)NLS_NUMERIC_CHARACTERS 的固定值,即 POINT 或 COMMA,因为此参数具有一个会话范围,由 Oracle 客户端应用程序定义。

在简历中,当您在 Oracle 视图中使用 TO_NUMBER() 函数而未指定任何 NLS_NUMERIC_CHARACTERS 时,您的视图将正常工作,直到它在具有另一个不同 [=15] 的会话中执行=]价值!

当您尝试使用 Oracle TO_DATE() 函数转换保存为字符串的 DATE 值时,同样的事情肯定存在!

为什么是模棱两可的!

为了回答评论中的一些问题,我在下面的段落中添加了我的长篇解释。

在会话级别或系统级别更改 NLS_NUMERIC_CHARACTERS 并不总是可行的,因为 NLS_NUMERIC_CHARACTERS 有 2 个不同的角色或目标。

  1. 修复十进制数字的小数分隔符显示
  2. 固定用于在 TO_NUMBER() 中转换字符串的小数点分隔符 函数

示例:假设您将创建一个显示小数的视图,并使用 TO_NUMBER() 进行一些计算。

如果数据库中保存的小数点分隔符是POINT,需要显示小数点时使用COMMA作为小数点分隔符,可以修改NLS_NUMERIC_CHARACTERS定义小数点分隔符为POINT。 Oracle TO_NUMBER() 函数将正常工作,但屏幕上显示的小数将以 POINT 作为小数点分隔符显示!

这就是为什么我说 NLS_NUMERIC_CHARACTERS 是模棱两可的。

在简历中,在 NLS_NUMERIC_CHARACTERS 可以是 '.,' 或 ',.' 的数据库系统中并且十进制数可以使用固定格式保存在 Oracle table 中(例如:小数点分隔符是 POINT)使用 TO_NUMBER() 而不指定正确的 NLS_NUMERIC_CHARACTERS 是不安全的。

Oracle 错误(如果您愿意,也可以是误解)是定义了一个具有 2 个不同角色的参数 (NLS_NUMERIC_CHARACTERS)!

好吧,在我看来,最好的选择是将数字保留在 NUMBER 数据类型列中。您将如何将 TO_NUMBER 应用到 value = '32.5rzg'

除此之外,我认为您已经发现的(应用 NLS_NUMERIC_CHARACTERS)是您可以做的,除了使用 REPLACE 并将这些字符串值中的所有点都转换为逗号。

作为参考,我已经创建了跟随函数

CREATE FUNCTION TO_X_NUMBER(sNumber IN VARCHAR2) 
  RETURN NUMBER as

BEGIN
    RETURN TO_NUMBER
        (sNumber
        ,'99999999999999999999D99999999999999999999'
        ,'NLS_NUMERIC_CHARACTERS=''.,''');
END TO_X_NUMBER;

和同义词

CREATE PUBLIC SYNONYM TO_X_NUMBER FOR GWHDBA.TO_X_NUMBER; 

GRANT EXECUTE ON TO_X_NUMBER TO PUBLIC; 

我现在可以从另一个模式使用它,如下例所示:

SELECT TO_X_NUMBER(PARAM_VALUE) as STANDARD_DEVIATION
  FROM TB_PARAMETER
  WHERE PARAM_NAME = 'STANDARD_DEVIATION';