如何处理由于分组函数而导致的 JDBC 数字类型的精度损失
Posted
技术标签:
【中文标题】如何处理由于分组函数而导致的 JDBC 数字类型的精度损失【英文标题】:How to handle the loss of precision on JDBC numeric types due to grouping functions 【发布时间】:2018-02-01 22:56:35 【问题描述】:Oracle(和其他一些数据库)有一个数据类型 NUMBER,可以选择使用它设置精度和小数位数。
假设以下查询:
SELECT agent_code,
AVG (opening_amt)
FROM customer
GROUP BY agent_code;
如果上述查询中的两个字段都定义为 NUMBER(12,0),则 JDBC 中的结果确实是 agent_code 的结果,但在“AVG(opening_amt)”上,精度和比例都返回 0(通过 java.sql.ResultSetMetaData .getPrecision(col) 和 java.sql.ResultSetMetaData.getScale(col) 。
这与 NUMBER 基本相同,没有任何精度或比例规范,根据 oracle,将等于 NUMBER(38,12)。
上面的精度损失给了我一个判断sql类型应该转换为Double还是Integer的问题。
所以,我想知道这是否真的是 Oracle 的 JDBC 驱动程序中的一个错误,或者应该如何处理? (不,使用 BigDecimal 作为对应的 java 类型对我来说不是一个选项)。
【问题讨论】:
仅仅因为原始值可能被定义为NUMBER(12,0)
,并不意味着它们的平均值必须是相同的类型。事实上,如果您需要尽可能精确地获得平均值,那将是一件非常糟糕的事情。如果您想控制它,您可以将平均值转换为特定类型。
同意。但是 JDBC 不应该返回字段的最大精度和规模(就像 Oracle 在内部处理它),而不是 0,0 ?
我希望如此。列的类型(通过ResultSetMetaData.getColumnType()
)真的是Types.NUMERIC
吗?
是的,java.sql.Types.NUMERIC (2)
嗯..我查看了 Postgres 驱动程序如何处理这种情况,它似乎做了完全相同的事情。对于没有指定精度或小数位数的NUMERIC
列,内部atttypmod
值设置为-1
,导致两种方法都返回0
,否则精度和小数位数基于atttypmod
计算。所以不仅仅是甲骨文应该归咎于此。
【参考方案1】:
这是基于 Postgres 驱动程序postgresql-9.4-1204-jdbc42.jar
中类似行为的推测。
对于未指定的NUMERIC
,数据库似乎没有存储有关列的精度和比例的任何特定信息。这允许数据库以任何合适的方式在内部存储值。来自https://www.postgresql.org/docs/current/static/datatype-numeric.html
不带任何精度或小数位数创建一个列,其中可以存储任意精度和小数位数的数值,达到精度的实现限制(小数点前最多 131072 位;小数点后最多 16383 位)
由于驱动程序不知道服务器具体实现的最大值是多少,它不能返回实际值。它返回 0 表示它不知道实际值,并且不想做出任何有根据的猜测。
似乎情况是same with Oracle。最大精度可能更高,但可移植性只能保证最多 38 位。
几乎可以存储任何数量级的数字,并保证可在运行 Oracle 数据库的不同系统之间移植,精度高达 38 位。
至于解决问题中的问题,就像 StanislavL 指出的那样,您可以通过强制转换将该值强制为特定的精度/比例。
【讨论】:
很好的调查!看来我们的猜测是合理的。 +1【参考方案2】:我认为您可以转换为任何所需的类型
CAST(AVG(opening_amt) AS DECIMAL(12,2))
见the example
SQL AVG() 函数返回带有默认小数位的平均值。 CAST() 用于增加或减少值的小数位。在转换小数和数字数据类型时,CAST() 函数在保留小数位方面要好得多。后跟格式规范的“AS DECIMAL”与 CAST() 一起使用,用于将数值转换为特定的小数位值。
【讨论】:
是的,但这仍然留下了一个问题,即为什么 Oracle 将比例和精度返回为 0,而它在内部使用这两个 (38,12) 的最大可用值。 我认为它会根据结果实时计算规模和精度。例如。如果您有 NUMBER(1) 并且总和 10 行,则结果可能是 NUMBER(2) 在 10k 行的情况下......等等。所以我猜 oracle on fly 根据计算结果更改列类型。这只是我的猜测,但没有参考任何文档。以上是关于如何处理由于分组函数而导致的 JDBC 数字类型的精度损失的主要内容,如果未能解决你的问题,请参考以下文章
在 TextReconiger 上实例化期间,我如何处理由于 Google Ocr lib 而导致的应用程序中的本机崩溃?