Arithmetic overflow error converting numeric datatype to numeric - sql

I'm having following columns in a table
SIZE NUMERIC(14,5)
PRICE NUMERIC(14,5)
when I perform this select query,
SELECT SIZE,
PRICE,
SIZE*PRICE AS TOTAL
FROM TNAME
I'm getting results:
1.00000 90.00000 90.0000000000
1.00000 90.00000 90.0000000000
1.00000 90.00000 90.0000000000
1.00000 100.00000 100.0000000000
1.00000 30.00000 30.0000000000
I'm wondering why the third column is returning with 10 digits after decimal point?
Also I'm getting
Arithmetic overflow error converting numeric datatype to numeric
while inserting result into another table which has the same columns with same datatype
INSERT INTO TNAME2(SIZE, PRICE, TOTAL)
SELECT
SIZE, PRICE, SIZE * PRICE AS TOTAL
FROM
TNAME

Try This.
SELECT SIZE,
PRICE,
CAST(SIZE*PRICE AS numeric(14,5))AS TOTAL
FROM TNAME

INSERT INTO TNAME2
SELECT SIZE ,
Price ,
CAST(SIZE * PRICE AS NUMERIC(15, 5))
FROM TNAME
OR
INSERT INTO TNAME2 (SIZE,Price,TOTAL)
SELECT SIZE ,
Price ,
CAST(SIZE * PRICE AS NUMERIC(15, 5)) AS TOTAL
FROM TNAME

Regarding the first question, the number of decimals, there is nothing wrong. It's the basic, everyday multiplication we learn at school: Multiplying two decimal numbers produces a number with as many decimal digits as the sum of decimal digits of the operands. The number of integer digits can be up to the sum of integer digits of the operands.
Multiplying 10.11 by 12.13 produces 122.6343. It would be VERY awkward if SQL Server broke this basic rule and arbitrarily truncated the result.
As a result, when you try to store the product in a column that accepts fewer digits, you get an overflow error. SQL Server won't change the number of digits automatically because there is no way to decide the correct action.
There are a lot of ways you can handle this, depending on the loss of precision you are willing to suffer:
Truncate the extra digits, ie throw them away, accepting up to a unit loss. This can become a LOT of money if you store totals.
Round to the desired number of digits. Sounds intuitive, but what about half-way values, ie 0.00005 in your case? Should it be 0.0001 or 0.0000? So we have
Rounding up, where 0.5 becomes 1, resulting in up to .5 loss per entry
Down, when it becomes 0, with the same loss
Round to even or odd, where you round to the nearest odd or even number, which on average produces minimal loss. While this sounds weird, it is the standard defined in IEEE 754. It's also called banker's rounding because it's used in bookkeeping to minimize losses due to truncation.
If you want to store fewer digits you need to decide whether you need to truncate the extra digits or how to round the number, then do it yourself in code.
In your case, you can use CAST to a numeric of the desired precision. This will perform rounding half up, where 0.00005 becomes 0.0001, eg:
INSERT INTO TNAME2(SIZE, PRICE, TOTAL)
SELECT
SIZE, PRICE, CAST(SIZE * PRICE as numeric(14,5)) AS TOTAL
FROM
TNAME
SQLFiddle here
This will work, assuming the number of digits doesn't exceed 14, otherwise you will have to change the type of your table field.
If you want some other kind of rounding in SQL, you will have to create your own function.

Got below useful link.
Multiplication of numerics
As per this link you can try as query as below
SELECT SIZE,
PRICE,
CAST(SIZE*PRICE AS numeric(28,5))AS TOTAL
FROM TNAME

Try this
SELECT SIZE,PRICE,CONVERT(numeric(14,5), SIZE*PRICE) AS TOTAL
FROM TNAME
Write the same query in insert, it must work

Related

How to return a number with two decimal places in SQL Server without it automatically rounding

This is an odd one, I have a cost price, and an average cost price. The avg is determined by the avg function and compared to the cost price it looks like the below:
Cost Price | Average Cost Price
8.24 8.23897666
Now when i use the below code it changes it to 8.24 even though i am not specifying any round function
select cast(8.23897666 as numeric(18,2))
I've tried casting it as a float and still it rounds it to .24 even though i want it to only return 2 decimal places.
Can anyone shed any light on this please as i am unsure as to why this is happening? AS regardless of the number before the decimal place i was to return the full number and 2 decimal places.
You can use floor() and integer division:
select floor(8.23897666 * 100) / 100
Or better yet, use round() with a non-0 third argument:
select round(8.23897666, 2, 1)

Casting as decimal not acting as expected

I am new to SQL and I am trying to calculate percentage from two pre-calculated numbers. I want the percentage to be at 2 decimal points. The numbers are integers so I am casting them as decimals before the percentage calculation. The calculation is simply
(Risk1/GrandTotal *100) both from the same table.
A piece of the code is given below:
SELECT risk_date_day,
((CAST(Risk1 as decimal(38,2))/CAST(GrandTotal as decimal(38,2))) * 100) FROM DPRAT2_Export
The result shows 6 numbers after the decimal
I have tried many different numbers for the parameters of decimal(x,x). I understood from research that the 2nd number is scale and that specifies the number of places after the decimal. I am not sure why the result has 6 decimal places. Please help. Thanks!
Do the cast() after the division:
SELECT risk_date_day,
CAST(Risk1 * 100.0 / GrandTotal as decimal(38, 2))
FROM DPRAT2_Export;
SQL in general and SQL Server in particular has very arcane rules for determining the number of decimal points in the result of mathematical operations on decimals. So, just convert the result.
If there is the risk that GrandTotal might be zero, I would advise:
SELECT risk_date_day,
CAST(Risk1 * 100.0 / NULLIF(GrandTotal, 0) as decimal(38, 2))
FROM DPRAT2_Export;
This prevents the divide-by-zero error, returning NULL instead.

How to calculate decimal(x, y) max value in SQL Server

How do I know the maximum value of decimal type?
For example:
decimal(5, 2)
Can you please explain the mechanism of decimal type?
The maximum possible value can be calculated using the following maths:
(10 ^ (x-y)) - (10 ^ -y)
So in your initial example
(10 ^ (5-2)) - (10 ^ -2) = 1000 - 0.01 = 999.99
I'm adding this answer as my google results took me here when trying to remind myself how to calculate the SQL friendly upper bounds of a C# decimal going into an SQL (28,10) decimal field.
999999999999999999.9999999999 by the way.
You can use an aggregate function in order to get the maximum value in a column
SELECT MAX(myColumn) AS MyColumnMax
FROM myTable
If you want to know the maximum value for each customer for instance, you can group by this customer
SELECT CustomerID, MAX(OrderAmount) AS MaxOrderAmount
FROM CustomerOrders
GROUP BY CustomerID
You can find other aggregate functions here.
If, by contrast, you are interested in range of a decimal type, then consider, that you are declaring the total number of digits and decimals. Therefore the maximum number is reached, when all these digits are 9.
So for decimal(5,2) it would be 999.99. 5 is the total number of decimals to the left and to the right of the decimal point. 2 is the number of decimals to the right of the decimal point.
The maximum possible range for decimals is -10^38 + 1 through 10^38 - 1.

Sum function doesn't give sufficient decimal places with divide function

I have the following sql query that I am running:
select sum(cast(mkt_value as decimal(20,7)))/1204438043.37 from table1
and I get the following result which is correct but I need at least 10 decimal places not 6:
0.009347
If I run the following query:
select sum(mkt_value) from table1
I get 11258490.2400.
If I divide 11258490.24 by 1204438043.37 in excel I get 0.009347504674 which is the answer I'm looking for.
Please help me correct my SQL!!
Your cast is breaking this. It doesn't have enough space to give you more than six decimal places. What you're saying is literally "give me the result of this division with at most six decimal places", and then you're suprised the result only has six decimal places :)
The solution is either to omit the cast (if the data type is money, it's fine) or increase the scale of the decimal, eg. decimal(20, 11). The second parameter of the decimal type says the maximal amount of decimal places (-1) in the number. Also, consider only casting the result of the sum instead of all the items.
Note that most operations in MS SQL return a value of the same data type. So:
select 3 / 4; -- Returns 0
select cast(1000 as smallint) * cast(1000 as smallint);
-- Error - 1 000 000 is too big for smallint
The same thing happens in your sum and also in the division that happens right after it :)
You need to apply casting after calculating final value
select CAST (sum(mkt_value)/1204438043.37 as decimal(15,10)) from table1.
This means result would be having maximum 15 digits and mandatory 10 decimal places.
I would suggest the following query,Since your final decimal places are getting truncated.Hence a cast would be required to your final result set.
select
cast(cast (sum(mkt_value) as decimal(20,7))/cast (1204438043.37 as decimal(20,7)) as decimal(20,12)) from table1
Thank you..Let me know if this works

SQL SUM - I don't want to loose precision when summing floating point data

I know when you work with Money it's better (if not imperative) to use Decimal data type, especially when you work with Large Amount of Money :). But I want to store price of my products as less memory demanding float numbers because they don't really need such a precision. Now when i want to calculate the whole Income of the products sold, it could become a very large number and it must have great precision too. I want to know what would be the result if I do this summation by SUM keyword in a SQL query. I guess it will be stored in a Double variable and this surely lose some precision. How can I force it to do calculation using Decimal numbers? Perhaps someone who knows about the internals of SQL engines could answer my question. It's good to mention that I use Access Database Engine, but any general answer would be appreciated too. This might be an example of the query I would use:
SELECT SUM(Price * Qty) FROM Invoices
or
SELECT SUM(Amount) FROM Invoices
Amount and Price are stored as float(Single) data type and Qty as int32.
If you want to do the calculation as a double, then cast one of the values to that type:
SELECT SUM(cast(Price as double) * Qty)
FROM Invoices;
SELECT SUM(cast(Amount as double))
FROM Invoices;
real double precision
Note that naming is not consistent among databases. For instance "binary_float" is 5 bytes (based on IEEE 4-byte float) and "binary_double" is 9 bytes (based on IEEE 8-bytes double). But, "float" is 8-bytes in SQL Server, but 4-byte in MySQL. SQL Server and Postgres use "real" for the 4-byte version. MySQL and Postgres use "double" for the 8-byte version.
EDIT:
After writing this, I saw the reference to Access in the question (this should really be a tag). In Access, you would use cdbl() instead of cast():
SELECT SUM(cdbl(Price) * Qty)
FROM Invoices;
SELECT SUM(cdbl(Amount))
FROM Invoices;
If the choice is between a float and a 4-byte (unsigned) int (both requiring the same amount of storage in memory) there are pros and cons:
The float cannot accurately handle cents assuming that a price has
the format $$$$$.cc (1/100ths are not precisely representable in the
floating-point - single as well as double - format), so this will
introduce rounding errors which are usually unacceptable in
money-related applications.
The int - assuming that you express the price in cents - will allow
precise values in the range -2^31 to 2^31-2^0 (about 2 * 10^9) cents for signed values
and 0 to 2^32-2^0 (about 4 * 10^9) for unsigned. The downside is that it may feel
"unnatural" to use cents instead of dollars and cents but this is
mostly a problem inside the developers mind: the actual "problems" -
if you wish to call them that - arise when printing the values in
dollars and cents which require a slightly more complex formatting
but this is a very small price in relation to how the rest of the
application can be simplified.
Later, when summing or performing other calculations - the integer cent and quantity values are first converted to double precision floating-point. The double precision format allows expressing integer values (assuming integer cents) precisiely in the range -(2^53-2^0) to 2^53-2^0 which probably (you need to check) covers your needs. Keep in mind, though, that you will still have integer cents in the double which need to be converted to dollars and cents.
EDIT_______________________
"6-7 decimal digits of precision" is most easily explained by the range of integers representable in the single-precision format. Since the SP format significand is 24 bits long (1 implicit + 23 explicit) this allows integers in the range 2^0 to 2^24-2^0 or 1 to 16777215. 16777215 is more than six (999999) but less than seven (9999999) decimal digits, hence "6-7 decimal digits." The double-precision format features a 53 bit significand (1 + 52) which results in an integer range of 2^0 to 2^53-2^0.
The real SP precision is "24 sequential binary digits of precision."
If you can make do with cents in 50 unit increments your range in SP will be 2^-1 to 2^23-2^-1 or 0.5 to 8388607.5
If you can make do with cents in 25 unit increments your range in SP will be 2^-2 to 2^22-2^-2 or 0.25 to 4194303.75.
Actually, as #Phylogenesis said in the first comment, when I think about, we don't sell enough items to overflow the precision on a double value, just like items are not expensive enough to overflow the precision on a float value.As I guessed, I tested and found that if you run simple SELECT SUM(Amount) FROM Invoices query, the result will be a double value. But following what suggested by #Gordon Linoff, the safest approach for obsessive-compulsive people is to use a cast to Decimal or Currency(Access). So the query in Access syntax will be:
SELECT SUM(CCur(Price) * Qty)
FROM Invoices;
SELECT SUM(CCur(Amount))
FROM Invoices;
which CCur function converts Single(c# float) values to Currency(c# decimal). Its good to know that conversion to Double is not necessary, because the engine does it itself. So the easier approach which is also safe is to just run the simple query.