How to handle the loss of precision on JDBC numeri

2019-05-06 14:26发布

问题:

Oracle (and some other DB's) have a datatype NUMBER, with which one can optionally set the precision and scale.

Suppose the below query:

SELECT agent_code, 
AVG (opening_amt)
FROM customer 
GROUP BY agent_code;

If both fields in above query were defined as NUMBER(12,0), the result in JDBC is indeed that for agent_code, but on "AVG(opening_amt)" both precision and scale return 0 (via java.sql.ResultSetMetaData.getPrecision(col) and java.sql.ResultSetMetaData.getScale(col) .

That's basically the same as NUMBER, without any precision or scale specification, and according to oracle, would equal NUMBER(38,12).

The above loss of precision gives me a problem to determine if the sql type should be converted to Double or Integer.

So, I was wondering if this is actually a bug in Oracle's JDBC driver, or how this should be handled? (and no, using BigDecimal as corresponsting java type is not an option for me).

回答1:

I think you can cast to any desired type

CAST(AVG(opening_amt) AS DECIMAL(12,2))

See the example

The SQL AVG() function returns the average value with default decimal places. The CAST() is used to increase or decrease the decimal places of a value. The CAST() function is much better at preserving the decimal places when converting decimal and numeric data types. The 'AS DECIMAL' followed by the format specification is used with CAST() for making a numeric value to a specific decimal place value.



回答2:

This is speculation based on similar behaviour in the Postgres driver postgresql-9.4-1204-jdbc42.jar.

For an unspecified NUMERIC the database doesn't seem to store any particular information on the precision and scale of the column. This allows the database to internally store the value in any way it seems fit. From https://www.postgresql.org/docs/current/static/datatype-numeric.html

without any precision or scale creates a column in which numeric values of any precision and scale can be stored, up to the implementation limit on precision (up to 131072 digits before the decimal point; up to 16383 digits after the decimal point)

Since the driver doesn't know what the implementation specific maximum of the server is, it can't return the actual values. It returns 0 to indicate that it doesn't know the actual values, and doesn't want to make any educated guesses.

Seems like the situation is the same with Oracle. The max precision may be higher, but portability is guaranteed only up to 38 digits.

Numbers of virtually any magnitude can be stored and are guaranteed portable among different systems operating Oracle Database, up to 38 digits of precision.

As for solving the issue in the question, like StanislavL indicated you can force the value to a specific precision/scale by casting.