在将应用程序移植到 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 不适用于函数调用,正如我们在这里看到的,它需要是隐式的。麻烦的是是每对类型只能定义一个转换,并且系统需要 float4->numeric 和 float8->numeric 赋值转换,不能丢弃。因此,使这些强制转换为隐式的唯一方法是update pg_cast set castcontext = 'i' where castsource in (700,701) and casttarget = 1700
.
现在我们正在破解目录,这表明这可能是一个坏主意。但我没有任何确凿的证据表明它是坏的。浮点数/双精度值是不精确的,而数值是精确的,因此在我看来,从前者转换为后者将完全保留数据,因此在逻辑上是安全的。我知道的唯一潜在问题是将歧义引入其他函数的参数模式,但提出这个问题的目的主要是为了找出我不知道的潜在问题。
那么,将 float->numeric 转换行为从 ASSIGNMENT 更改为 IMPLICIT 是否危险?
请注意,不同的舍入算法用于不同的数据类型。
https://www.postgresql.org/docs/current/static/datatype-numeric.html#DATATYPE-NUMERIC-TABLE
是的,侵入目录是不好的。原因 #1 是,如果您升级到新版本并忘记移动 hack,事情就会开始崩溃。只是运行 pg_dump 并在另一个实例上加载到相同的版本也将失去黑客攻击。新版本的 Postgres 也总是有可能发生如此大的变化,以至于您现在无法破解并迫使您返回并重新设计。
用你自己的函数覆盖是正确的方法。
简单的解决方案是投射您的价值:
结果
numeric
当然是数据类型。再次投射,如果您需要:如果必须,请创建一个您描述的包装函数。PostgreSQL 支持函数重载。 永远不要为此破解目录 - 请参阅@Matthew's answer了解基本原理。