如何将BigDecimal与ActiveRecord十进制字段进行比较?

时间:2011-06-09 18:04:30

标签: ruby-on-rails activerecord bigdecimal

假设这样的架构:

create_table "bills", :force => true do |t|
  t.decimal  "cost", :precision => 10, :scale => 5
end

我想编写一个函数,如果它是唯一的,就会将新帐单写入数据库。以下不起作用:

def load_bill_unless_exists(candidate)
  incumbents = Bill.scoped.where(:cost => candidate.cost)
  candidate.save unless incumbents.exists?
end

因为现任账单和候选账单的BigDecimal表示具有不同的限制,因此:cost => candidate.cost测试失败。也就是说,它正在比较:

candidate: #<Bill id: nil, cost: #<BigDecimal:105e39850,'0.1670576666 6666666E4',27(27)>>

incumbent: #<ServiceBill id: 198449, cost: #<BigDecimal:105e35840,'0.167057667E4',18(18)>>

请注意,候选人的BigDecimal表示比现任者更多的数字。

所以问题很简单:执行此比较的正确方法是什么?我考虑过:cost => BigDecimal.new(candidate.cost.to_s, 18),但这感觉不对 - 例如,18号来自哪里?

2 个答案:

答案 0 :(得分:1)

如果像你正在考虑作品一样,你可能不得不接受它。您正在使用的查询只是构建“WHERE cost = number”,如果数据库无法与传递的数字正确比较,则需要以不同方式传递它。看起来这是数据库阻止你而不是Rails中的任何东西。

如果您不喜欢在查询中进行投射,可以随时在模型中执行:

def cost_with_incumbent_precision
  BigDecimal.new(cost.to_s, 18)
end

答案 1 :(得分:1)

尝试使用BigDecimal#round

def load_bill_unless_exists(candidate)
  incumbents = Bill.scoped.where(:cost => candidate.cost.round(5))
  candidate.save unless incumbents.exists?
end

来自文档:

  

舍入到最接近的1(默认情况下),将结果作为BigDecimal返回。如果n指定为正数,则结果的小数部分不会超过该数字。

鉴于您已在模式中指定了5的精度,这是您在进行比较时应该舍入的内容。