How to avoid PG::NumericValueOutOfRange when using sum function - sql

I have method like this:
def self.weighted_average(column)
sql = "SUM(#{column} * market_cap) / SUM(market_cap) as weighted_average"
Company.select(sql).to_a.first.weighted_average
end
When the column is a decimal, it returns a value without problem.
But when the column is integer, the method ends up with a PG::NumericValueOutOfRange error.
Should I change column type integer to decimal, or is there a way to get the result of sum without changing column type?

You can always make float from your integer.
def self.weighted_average(column)
column = column.to_f
sql = "SUM(#{column} * market_cap) / SUM(market_cap) as weighted_average"
Company.select(sql).to_a.first.weighted_average
end

You can cast your value to alway be a decimal value, thus no need to change the column type:
sql = "SUM(#{column} * CAST(market_cap as decimal(53,8))) / SUM(CAST(market_cap as decimal(53,8))) as weighted_average"
P.S. I would go with changing the column type - it is consistent then.

I would suggest you to change the datatype to decimal. Because, when SUM gets PG::NumericValueOutOfRange, it means that your datatype is not sufficient. It will lead to gracefully handle this scenario, instead of a workaround.

Postgres documentation says this about SUM() return type:
bigint for smallint or int arguments, numeric for bigint arguments,
otherwise the same as the argument data type
This means that you will somehow need to change datatype that you pass to SUM. It can be one of the following:
Alter table to change column datatype.
Cast column to other datatype in your method.
Create a view that casts all integer columns to numeric and use that in your method.

You are trying to place a decimal value into a integer parameter. Unless you use the ABS() value that will not be possible, unless you are 100% sure that the % value will always be 0.
Use type Float or function ABS() if you HAVE to have an INT

Yo could try casting column to decimal
sql = "SUM(CAST(#{column}) AS DECIMAL * market_cap) / SUM(market_cap) as weighted_average"

Related

How to multiply string value to longint in SQL

I have the below data which I want to multiply together, column A times column B to get column C.
A has datatype string and B has datatype long.
A B
16% 894
15% 200
I have tried this expression in query cast(A as int)*B but it is giving me an error.
You can try below way -
select cast(left(A, patindex('%[^0-9]%', A+'.') - 1) as int)*B
from tablename
You need to remove the '%' symbol before attempting your cast. And assuming you are actually wanting to calculate the percentage, then you also need to divide by 100.00.
cast(replace(A,'%','') as int)/100.00*B
Note: You need to use 100.00 rather than 100 to force decimal arithmetic instead of integer. Or you could cast as decimal(9,2) instead of int - either way ensures you get an accurate result.
You may well want to reduce the number of decimal points returned, in which case cast it back to your desired datatype e.g.
cast(cast(replace(A,'%','') as int)/100.00*# as decimal(9,2))
Note: decimal(9,2) is just an example - you would use whatever precision and scale you need.
The syntax of the cast in SQL Server is CAST(expression AS TYPE);
As you cannot convert '%' to an integer so you have to replace that with an empty character
as below:
SELECT cast(replace(A,'%','') AS int);
Finally you can write as below:
SELECT (cast(replace(A,'%','') AS int)/100.00)*B as C;

Placeholder for Decimal Column in SQL

I am new to SQL so this question is likely simple and easy to answer.
I am creating a temp table in which I want a blank column to be filled later with decimal values.
Can I use as a placeholder in my SELECT statement to indicate that I want decimal values to fill the column?
For columns that I will fill with integer values, I am using the following code:
SELECT 0 AS ColumnName
I do not believe this will work for the column that I want filled with decimal values, as I believe the 0 indicates integer values instead. Is there something that I can use instead of the 0?
Any help would be appreciated!
SQL will perform implicit conversion of some types when it knows there is no risk of data loss. In your case, int can safely convert to decimal, because there's no way to corrupt your data. 0 is 0.0 as far as SQL is concerned.
The opposite would not be true, as casting from decimal to int would lose the decimal part. Therefore SQL would not implicitly cast the opposite.
Your query is good as is.
Using a 0 in a float field will not cause truncation on the record since it contains less precision than a float, so it is safe to do this and implies a 0.0
You could also DEFAULT the field to 0.0 in the table declaration so it will be 0.0 until updated. But not required.
Ex: CREATE TABLE table
( col1 FLOAT(size,d) DEFAULT 0.0 )

sqlserver sum with minus

I use sqlserver 2012.
I have a query like this
SELECT SUM(TH.CLEAVE_EARN_DAY), SUM(TH.CLEAVE_DAY),
SUM(TH.CLEAVE_EARN_DAY) - SUM(TH.CLEAVE_DAY)
FROM TH_LEAVE_CARD TH
The result is 0, 14.5, -15
so -15 is wrong. Must be -14.5
any suggestion ?
This is what you can try
SELECT SUM(TH.CLEAVE_EARN_DAY), SUM(TH.CLEAVE_DAY),
SUM(TH.CLEAVE_EARN_DAY)*1.0 - SUM(TH.CLEAVE_DAY)
FROM TH_LEAVE_CARD TH
Multiplying with 1.0 will just give you back decimal value and taking away will give you what you asked for
Try converting all arguments to the same datatype and then do calculation:
SELECT
SUM(CAST(TH.CLEAVE_EARN_DAY AS DECIMAL(18,2))),
SUM(CAST(TH.CLEAVE_DAY AS DECIMAL(18,2))),
SUM(CAST(TH.CLEAVE_EARN_DAY AS DECIMAL(18,2))
- CAST(TH.CLEAVE_DAY AS DECIMAL(18,2))) AS substraction
FROM TH_LEAVE_CARD TH
Also you can combine:
SUM(TH.CLEAVE_EARN_DAY) - SUM(TH.CLEAVE_DAY)
to (if both column are NOT NULL):
SUM(TH.CLEAVE_EARN_DAY - TH.CLEAVE_DAY)
or (thanks Arvo for pointing this):
SUM(ISNULL(TH.CLEAVE_EARN_DAY,0) - ISNULL(TH.CLEAVE_DAY,0))
To perform mathematical operations on columns:
Used columns should be converted into same numeric/decimal data type.
To handle null values you may use ISNULL function.
Ex:
SELECT SUM(TH.CLEAVE_EARN_DAY), SUM(TH.CLEAVE_DAY),
SUM(cast (TH.CLEAVE_EARN_DAY) as decimal(5,1)) - SUM(cast ( (TH.CLEAVE_DAY) as decimal(5,1))
FROM TH_LEAVE_CARD
There is few reason why the result is not as per what you are expecting. In Sql Server any math operation that contains a null would result to null. for example sum(1,2,3,null,4) is equal to null. 1 + null also equal to null.
therefore it would be safer to use isnull function to assign a default value in case the value is null.
for mathematical operation. sql server would do the calculation based on the specified data type. for example int / int = int. therefore the result would be missled. because most of the time int / int = float.
it would be better to change the value to double prior to do any arithmetic operation.
below is the example after include the isnull function as well as cast to float.
SELECT SUM(CAST(ISNULL(TH.CLEAVE_EARN_DAY,0) as double)), SUM(cast(ISNULL(TH.CLEAVE_DAY,0) as double)),
SUM(cast(ISNULL(TH.CLEAVE_EARN_DAY,0) as double)) - SUM(cast(ISNULL(TH.CLEAVE_DAY,0) as double))
FROM TH_LEAVE_CARD TH

PostgreSQL sum typecasting as a bigint

I am doing the sum() of an integer column and I want to typecast the result to be a bigint - to avoid an error. However when I try to use sum(myvalue)::bigint it still gives me an out of range error.
Is there anything that I can do to the query to get this to work? Or do I have to change the column type to a bigint?
The current manual is more explicit than it used to be in 2013:
sum ( integer ) → bigint
If your column myvalue indeed has the type integer like you say, then the result is bigint anyway, and the added cast in sum(myvalue)::bigint is just noise.
Either way, to get an "out of range" error, the result would have to be bigger than what bigint can hold:
-9223372036854775808 to +9223372036854775807
One would have to aggregate a huge number of big integer values (>= 2^32 * 2^31). If so, cast the base column to bigint, thereby forcing the result to be numeric:
SELECT sum(myvalue::int8) ...
The more likely explanation is that your column has, in fact, a different data type, or the error originates from something else. Not enough detail in the question.
I solved my problem using following statement
SUM(CAST(gross_amount AS Integer))
This is give the result of the column as SUm bigint,
Note:My column gross_amount was double type.
You need to cast it before doing the operation:
SUM(myvalue::bigint)

multiple a bigint column and a decimal column in a table cell

I have a field of bigint and a field of decimal in data base and i would like to multiple those fields in a tablecell in reportingservices.
My code is like this:
(Fields!dpr_unitprice.Value) * (Fields!dpr_PurchasedQuntity.Value)
but the result is not correct.
How can i get the correct result?
It might be that your DB engine is returning a bigint.
Cast the bigint value as a decimal before multiplying the two.
You have to cast both fields to the unit you want to work with, so if you want to work with decimal then:
cdec((Fields!dpr_unitprice.Value)) * (Fields!dpr_PurchasedQuntity.Value)