Friday, September 23, 2016

How to avoid PG::NumericValueOutOfRange when using sum function

Leave a Comment

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?

6 Answers

Answers 1

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 

Answers 2

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.

Answers 3

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.

Answers 4

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.

Answers 5

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

Answers 6

Yo could try casting column to decimal

sql = "SUM(CAST(#{column}) AS DECIMAL * market_cap) / SUM(market_cap) as weighted_average" 
If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment