Numeric overflow in insert query - firebird

I am receiving an error of:
Arithmetic overflow or division by zero has occurred. arithmetic
exception, numeric overflow, or string truncation. numeric value is
out of range.
This can be replicated with:
create table testing (avalue numeric(3,2));
and the following insert:
insert into testing values (328);
However, using the following works fine:
insert into testing values (327);
328 seems to be the magic figure the error occurs. To me, the numeric(3,2) declaration should allow me 000-999 with 2 decimal places but based on the above that is wrong.
Can someone explain why this is and what I should declare my domain as if I want to allow 0-999 with 2 decimal places as.
Thanks

328 is not "magic" number :)
The magic number is 32767 ( 0x7FFF). This is SMALLINT type limit.
Note : Firebird not support unsigned integer type.
Limit for NUMERIC type vary according to storage type and scale.
Internal storage type are SMALLINT, INTEGER and BIGINT according by precision as:
precision-type
1..4 - SMALLINT
5..9 - INTEGER
10..18 - BIGINT
So
NUMERIC(3,2) is SMALLINT internal type max 32767 / 100 = 327.67.
Update
Firebird 2.5 Language Reference
by
Paul Vinkenoog,
Dmitry Yemanov and
Thomas Woinke
contains more comprehensive description of NUMERIC type than other official Firebird documents.
NUMERIC (precision, scale) is the exact number with the decimal
precision and scale specified by the and .
Syntax:
NUMERIC [precision [, scale]]
The scale of NUMERIC is the count of decimal digits in the
fractional part, to the right of the decimal point. The precision of
NUMERIC is the total count of decimal digits in the number.
The precision must be positive, the maximum supported value is 18.
The scale must be zero or positive, up to the specified precision.
If the scale is omitted, then zero value is implied, thus
meaning an integer value of the specified precision, i.e.
NUMERIC (P) is equivalent to NUMERIC (P, 0). If both the precision and
the scale are omitted, then precision of 9 and zero scale are implied,
i.e. NUMERIC is equivalent to NUMERIC (9, 0).
The internal representation of the NUMERIC data type may vary.
Numerics with the precision up to (and including) 4 are always stored
as scaled short integers (SMALLINT). Numerics with the precision up to
(and including) 9 are always stored as scaled regular integers
(INTEGER). Storage of higher precision numerics depends on the SQL
dialect. In Dialect 3, they are stored as scaled large integers
(BIGINT). In Dialect 1, however, large integers are not available,
therefore they are stored as double precision floating-point values
(DOUBLE PRECISION).
The effective precision limit for the given value depends on the
corresponding storage. For example, NUMERIC (5) will be stored as
INTEGER, thus allowing values in the precision range up to (and
including) NUMERIC (9). So beware that the declared precision is not
strictly enforced.
Values outside the range limited by the effective precision are not
allowed. Values with the scale larger than the declared one will be
rounded to the declared scale while performing an assignment.

The declaration numeric(5, 2) gives you numbers from 0.00 to 999.99. The declaration numeric(3,2) gives you numbers from 0.00 to 9.99. This is sort-of illustrated here. But these are the standard declarations for numerics in SQL.
The "3" is the scale, which is the total number of digits in the number, not the number to the left of the decimal place.
I'm not sure why 327 is allowed.

Related

Rails: Storing high currency values in PostgreSQL

I would like to store high value currency values into the DB.
I tried to use an integer field in my migration, however I get following error
PG::NumericValueOutOfRange: ERROR: numeric field overflow
DETAIL: A field with precision 8, scale 2 must round to an absolute value less than 10^6.
So I then tried to use decimal with precision
t.decimal :value, precision: 30, scale: 2
Once gain I am getting the same error when I enter
What I would like to know, is it possible, and how can I save a value like 1000000.0 into the database.
I tried to use an integer field in my migration, however I get following error
PG::NumericValueOutOfRange: ERROR: numeric field overflow
DETAIL: A field with precision 8, scale 2 must round to an absolute value less than 10^6.
That's a misunderstanding. The error message is for data type numeric - numeric(8,2) to be precise - not integer.
Solution
Just use numeric without precision and scale. It stores decimal number (with any amount of fractional digits) exactly as given.
decimal is a synonym of numeric in Postgres.
If you don't have fractional digits, consider integer (max 2^31 - 1) or bigint (max 2^63 - 1).
Related:
PostgreSQL: Which Datatype should be used for Currency?

Postgres Custom float type that is always truncated to 2 decimals after point

Can I generate a custom data type in postgres that everytime I insert or update a float into it it is truncate to 2 decimals after dot.
create table money(
formatted moneys_type
);
insert into money values (30.122323213);
Select * from money;
Returns
30.12
Update I didn't use numeric or decimal because they round up when 1.999 => 2
See documentation on Numeric Types / Arbitrary Precision Numbers.
The precision of a numeric is the total count of significant digits in
the whole number, that is, the number of digits to both sides of the
decimal point. The scale of a numeric is the count of decimal digits
in the fractional part, to the right of the decimal point. So the
number 23.5141 has a precision of 6 and a scale of 4. Integers can be
considered to have a scale of zero.
...
To declare a column of type numeric use the syntax:
NUMERIC(precision, scale)
The maximum allowed precision when explicitly specified in the type declaration is 1000.
So you can use
NUMERIC(1000, 2)

In Postgres, Confusion for the conversion of Numeric/Real/Double Precision to Character Varying / Text

For the conversion of Numeric/Real/Double Precision to Character Varying / Text.
select (12.0/100)::Double Precision;# 0.12
select (12.0/100)::Double Precision::Text;# 0.119999999999999996
select 0.12::text ; # 0.12
select (12.0/100)::Numeric::Text ; #0.12000000000000000000
0.12::text is correct, but the result of (12.0/100)::Numeric::Text and (12.0/100)::Double Precision::Text are confusing.
The values might confuse you, but they are correct.
It seems like you set the parameter extra_float_digits to 3 (or you are using JDBC, which does that for you).
double precision is a floating point type and hence imprecise. With the default 0 for extra_float_digits you won't notice that, because the value is truncated so that only significant digits are shown, but if you request full precision, you'll see the rounding error in all its glory.
The last command will show many zeroes because you didn't specify numeric(10,2) to limit the number of decimal digits, and division potentially produces a lot of those. If you try * instead of /, you'll see what I mean.

Can the Postgres data type NUMERIC store signed values?

In PostgreSQL, I would like to store signed values -999.9 - 9999.9.
Can I use numeric(5.1) for this?
Or what type should I use?
You can certainly use the arbitrary precision type numeric with a precision of 5 and a scale of 1, just like #Simon commented, but without the syntax error. Use a comma(,) instead of the dot (.) in the type modifier:
SELECT numeric(5,1) '-999.9' AS nr_lower
, numeric(5,1) '9999.9' AS nr_upper;
nr_lower | nr_upper
----------+----------
-999.9 | 9999.9
The minus sign and the dot in the string literal do not count against the allowed maximum of significant digits (precision).
If you don't need to restrict the length, just use numeric.
If you need to enforce minimum and maximum, add a check constraint:
CHECK (nr_column BETWEEN -999.9 AND 9999.9)
numeric stores your number exactly. If you don't need the absolute precision and tiny rounding errors are no problem, you might also use one of the floating point types double precision (float8) or real (float4).
Or, since you only allow a single fractional decimal digit, you can multiply by 10 and use integer, which would be the most efficient storage: 4 bytes, no rounding errors and fastest processing. Just use and document the number properly.
Details for numeric types in the manual.

How to set precision and scale in ALTER TABLE

I have working code with PostgreSQL 9.3:
ALTER TABLE meter_data ALTER COLUMN w3pht TYPE float USING (w3pht::float);
but don't know how to set precision and scale.
The type float does not have precision and scale. Use numeric(precision, scale) instead if you need that.
Per documentation:
The data types real and double precision are inexact, variable-precision numeric types.
For your given example:
ALTER TABLE meter_data ALTER COLUMN w3pht TYPE numeric(15,2)
USING w3pht::numeric(15,2) -- may or may not be required
The manual:
A USING clause must be provided if there is no implicit or assignment cast from old to new type.
Example: if the old data type is text, you need the USING clause. If it's float, you don't.
As per PostgreSQL documentation, you can select the minimum number for the floating point numbers using syntax float(n) where n is the minimum number of binary digits, up to 53.
However, to store decimal values at all, use numeric(precision, scale) or its synonym decimal(precision, scale) but notice that these are hard limits; according to the documentation:
If the scale of a value to be stored is greater than the declared
scale of the column, the system will round the value to the specified
number of fractional digits. Then, if the number of digits to the left
of the decimal point exceeds the declared precision minus the declared
scale, an error is raised.
Thus your alter table could be:
ALTER TABLE meter_data
ALTER COLUMN w3pht TYPE numeric(10, 2)
USING (w3pht::numeric(10, 2));
for 2 digits right of decimal point and 10 total digits. However if you do not
need to specify limits, simple numeric will allow "up to 131072 digits before the decimal point; up to 16383 digits after".