将 float->numeric 转换从赋值更改为隐式的、危险的?

Noa*_*ter 7 postgresql best-practices datatypes cast

在将应用程序移植到 PostgreSQL (9.1) 时,我发现一个奇怪的 SQL 不兼容性与round()函数有关,特别是采用指示舍入精度的第二个参数的版本。

在 MySQL 中, round(some_float_column, 2)按预期工作,将some_float_column四舍五入的值返回到小数点后两位。在 Postgres 中,它错误ERROR: function round(double precision, integer) does not exist并建议HINT: No function matches the given name and argument types. You might need to add explicit type casts..

如文档所示... http://www.postgresql.org/docs/9.1/static/functions-math.html ...Postgres 有两个轮函数,round(value)它采用双精度数字,并round(value, precision)采用数字和一个整数。

所以,我不明白为什么 round 的两个参数形式不以 double 开头,但无论如何。在搜索中,我发现了这个问题的两种解决方案。一种是简单地创建我自己的版本round(value, precision),采用 (double, int) 并使用显式转换包装现有的 (numeric, int) 版本。这当然有效,但我不喜欢它(我的背景是 Oracle,它甚至没有真正的浮点类型)。在我看来,float/double 应该隐式转换为数字。事实证明,这些类型的 ASSIGNMENT 类型转换是预先定义的。但是 ASSIGNMENT 不适用于函数调用,正如我们在这里看到的,它需要是 IMPLICIT。与麻烦的是是每对类型只能定义一个类型转换,并且系统需要 float4->numeric 和 float8->numeric 赋值转换,不能删除。因此,使这些强制转换隐式的唯一方法是update pg_cast set castcontext = 'i' where castsource in (700,701) and casttarget = 1700.

现在我们正在入侵目录,这表明这可能是一个坏主意。但我没有任何确凿的证据表明它很糟糕。浮点/双精度值是不精确的,而数值是精确的,所以在我看来,从前者到后者的转换将完全保留数据,因此在逻辑上是安全的。我知道的唯一潜在问题是在其他函数的参数模式中引入歧义,但提出这个问题的目的主要是为了寻找我知道的潜在问题。

那么,将 float->numeric 转换行为从 ASSIGNMENT 更改为 IMPLICIT 是否危险?

Ale*_*lex 5

请注意,不同的舍入算法用于不同的数据类型。

https://www.postgresql.org/docs/current/static/datatype-numeric.html#DATATYPE-NUMERIC-TABLE

在舍入值时,数字类型舍入远离零,而(在大多数机器上)实数和双精度类型舍入到最接近的偶数。例如:

SELECT x,
  round(x::numeric) AS num_round,
  round(x::double precision) AS dbl_round
FROM generate_series(-3.5, 3.5, 1) as x;
  x   | num_round | dbl_round
------+-----------+-----------
 -3.5 |        -4 |        -4
 -2.5 |        -3 |        -2
 -1.5 |        -2 |        -2
 -0.5 |        -1 |        -0
  0.5 |         1 |         0
  1.5 |         2 |         2
  2.5 |         3 |         2
  3.5 |         4 |         4
Run Code Online (Sandbox Code Playgroud)


小智 3

是的,侵入目录是不好的。原因#1是,如果您升级到新版本并忘记移动黑客,事情就会开始崩溃。仅运行 pg_dump 并在另一个实例上加载到相同版本也会丢失该 hack。总有可能新版本的 Postgres 会发生很大的变化,以至于你现在无法进行黑客攻击,并迫使你返回并重新设计。

用你自己的函数覆盖是正确的方法。