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

yb3bgrhw  于 2023-01-01  发布在  Oracle
关注(0)|答案(3)|浏览(149)

今天,我发现Oracle NLS_NUMERIC_CHARACTERS的定义含糊不清!
实际上,从一开始,我就认为NLS_NUMERIC_CHARACTERS仅用于定义如何显示十进制数。
但实际上,NLS_NUMERIC_CHARACTERS也隐含在TO_NUMBER()函数中,用于定义要转换的STRING数的格式。
我的问题是,我是欧洲人,对我来说,十进制分隔符实际上是逗号(,),而不是点(.)。
当我显示一个数字时,我的首选项是使用逗号显示它。因此,NLS_NUMERIC_CHARACTERS被设置为',.',其中第一个字符定义数字分隔符。
直到现在,我对此没有意见:-)
我的问题是,所有String表的字段值包含一个数字是使用点字符作为小数点分隔符。
当我转换任何表示数字的字符串值时,我可以像这样使用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客户端应用程序定义。
在简历中,当您使用TO_NUMBER()函数而未在Oracle视图中指定任何NLS_NUMERIC_CHARACTERS时,您的视图将正常工作,直到在具有另一个不同NLS_NUMERIC_CHARACTERS值的会话中执行它!
当您尝试使用Oracle TO_DATE()函数转换保存为字符串的DATE值时,同样的事情肯定存在!

    • 为何如此暧昧**

为了回答评论中的一些问题,我在下面的段落中添加了我的详细解释。
在会话级别或系统级别更改NLS_NUMERIC_CHARACTERS并不总是可行的,因为NLS_NUMERIC_CHARACTERS具有两个不同的角色或目标。
1.修复十进制数的十进制分隔符显示
1.修复用于转换TO_NUMBER()函数中字符串的十进制分隔符
示例:假设您将创建一个视图,该视图将显示十进制数,并使用TO_NUMBER()进行一些计算。
如果数据库中保存的小数点分隔符为POINT,并且需要使用COMMA作为小数点分隔符来显示小数,则可以更改NLS_NUMERIC_CHARACTERS,将小数点分隔符定义为POINT。Oracle TO_NUMBER()函数将正常工作,但屏幕上显示的小数将使用POINT作为小数点分隔符来显示!
这就是为什么我说NLS_NUMERIC_CHARACTERS是不明确的。
在恢复中,在NLS_NUMERIC_CHARACTERS可以是.""或.""的数据库系统中,可以使用固定格式将十进制数保存在Oracle表中(例如:十进制分隔符为POINT),则在未指定正确的NLS_NUMERIC_CHARACTERS的情况下使用TO_NUMBER()是不安全的。
Oracle错误(或者误解,如果您愿意的话)是定义了一个具有两个不同角色的参数(NLS_NUMERIC_CHARACTERS)!

m1m5dgzv

m1m5dgzv1#

作为参考,我创建了以下函数

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';
pengsaosao

pengsaosao2#

从我的Angular 来看,最好的选择是在NUMBER数据类型列中保留数字,那么如何将TO_NUMBER应用到value = '32.5rzg'呢?
除此之外,我假设您已经发现(应用NLS_NUMERIC_CHARACTERS)的就是您可以做的,除了使用REPLACE并将所有字符串值中的点转换为逗号。

soat7uwm

soat7uwm3#

无论客户端NLS设置和输入字符串如何,始终执行正确操作的函数,

declare
  mynum  number;

  FUNCTION TO_NUMBER_FNLS(sNumber in varchar2) return number as
      ret  number;
  BEGIN
      select
           to_number(
             case substr(nls.value,1,1)
               when ',' then replace(sNumber, '.', ',')
               else          replace(sNumber, ',', '.')
             end
           )
           into ret
      from nls_session_parameters nls
      where nls.parameter = 'NLS_NUMERIC_CHARACTERS';

      return ret;
  END;

begin
  mynum := TO_NUMBER_FNLS('100.22');
  DBMS_OUTPUT.PUT_LINE('input with dot: '|| mynum);

  mynum := TO_NUMBER_FNLS('100,22');
  DBMS_OUTPUT.PUT_LINE('input with comma: '|| mynum);
end;
/

https://dbfiddle.uk/dim1wC2W

相关问题