如何了解PosgreSQL中的to_number格式

时间:2018-08-17 18:26:33

标签: sql postgresql formatting

基于对formatting documentation的阅读,我没有得到PostgreSQL函数“ to_number”所期望的行为。所以我可能读错了。有人可以解释一下,以便我知道在其他类似情况下会发生什么吗?

-- I find this intuitive:
# select to_number( '12,345.67', '99999.999') ;
 to_number 
-----------
  12345.67

-- I find this surprising:
# select to_number( '12,345.67', '99999.99') ;
 to_number 
-----------
   12345.6

-- EDIT: I found this surprising new variation:
# select to_number( '12,345.67', '999999.99') ;
to_number 
-----------
  12345.67

为什么在第二种情况下我最后的百分之一百位数掉了?

编辑:看来这个问题与四舍五入或以我的格式显示在小数点右边的位数无关。而是,该问题与格式包含的字符总数有关,因此与解析的字符总数有关。我认为最终的完整答案将是对mu简短发布的内容略有不同。

实际上,我总是可以返回比我想的还要多的数字。但这不是很令人满意。有一天它可能会咬我。注意:格式中的“ 9”和“ 0”不是问题。这些在to_number上的行为相同,我觉得有些惊讶...但是在上面的链接中有明确记录。

2 个答案:

答案 0 :(得分:0)

问题是您的“数字”用逗号作为千位分隔符,但您的模式却没有。垂直排列它们以使比较容易:

12,345.67
99999.99
  ^

我们看到模式正在寻找一个数字,但它找到了一个逗号。您的模式与您使用的字符串不完全匹配,因此您会得到意外的结果。

如果您将分隔符添加到模式中(请参见文档中的Table 9.26: Template Patterns for Numeric Formatting),那么您将获得所需的内容:

=> select to_number('12,345.67', '99,999.99');
 to_number 
-----------
  12345.67
(1 row)

答案 1 :(得分:0)

首先我要感谢亩。他的回答显然很有帮助。但是我要发布一个单独的答案,因为我认为他所说的答案错过了答案的重要部分。

我没有看过任何PostgreSQL代码,因此我的回答完全来自观察其行为。创建第一种格式时,我隐式地假设了以下内容:

# My pseudocode for select to_number( '12,345.67', '99999.99') ;
# I guessed PostgreSQL would do this:
1. Parse up to 5 digits
2. [optionally] find a decimal
3. [optionally] if decimal was found, find up to 2 more digits

in this example:
1. Up to five digits: 12345
2. Decimal: yes
3. Two more digits: 67
4. All together: 12345.67

# But in fact what it does is closer to this:
1. Parse up to 8 characters
2. Find the first decimal point in the parsed characters
3. In the set of parsed characters, find up to 5 characters before the decimal 
4. In the set of parsed characters, find up to 2 characters after the decimal.

in this example:
1. Up to 8 characters: 12,345.6
2. First decimal: the penultimate character
3. Before decimal: 12345  
4. After decimal:  6
5. All together: 12345.6

因此,我的问题从根本上来说是 PostgreSQL仅解析8个字符,但是我传递了9个字符。因此,解决方案是:

# Mu's suggestion: include comma in the format. Now the format is 9 characters.
# This way it parses all 9 characters:
select to_number('12,345.67', '99,999.99');
 to_number 
-----------
  12345.67

# Or include another character before the decimal
# This way it also parses 9 characters before limiting to 5.2:
select to_number( '12,345.67', '999999.99') ;
 to_number 
-----------
  12345.67

# Or include another character after the decimal
# This way it parses 9 characters before limiting to 5.3:
select to_number( '12,345.67', '99999.999') ;
 to_number 
-----------
  12345.67

一旦您这样看,就很清楚为什么难以理解的简并案例如此起作用:

# like this one work as they do:
select to_number('1x2,3yz45.67', '9999999.9999');
 to_number 
-----------
  12345.67

select to_number('12.3.45.67', '9999999.9999');
 to_number 
-----------
   12.3456

我不确定是否会指定这样的行为。但是现在更清楚了。