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

假设这样的架构:

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: #> 

 incumbent: #<ServiceBill id: 198449, cost: #> 

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

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

尝试使用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的精度,这是您在进行比较时应该舍入的内容。

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

如果您不喜欢在查询中进行强制转换,则可以始终在模型中执行此操作:

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