如何让rails以正确的数据类型而不是字符串返回SUM(columnName)属性?

假设查询以下表单

operatingExpenses = Expense.find(:all, {:select=>"categories.activityType, categories.name heading, sum(amount) totalAmount", :joins => "inner join expense_categories categories on category_id = categories.id ", :group => "categories.activityType, categories.name", :order => "categories.activityType, totalAmount DESC"} ) 

现在,amount被定义为数据库模式中的十进制字段。 例如,Rails迁移中的定义是

 create_table :expenses do |table| table.column :created_at, :timestamp, :null=>false table.column :description, :string, :limit=>100, :null=>false table.column :amount, :decimal, :scale=>2, :precision=>10, :null=>false table.column :category_id, :integer, {:null=>false, :default =>1} end 

现在,查询返回的记录集失败了以下断言

 assert_equal 800, operatingExpenses[1].totalAmount  expected but was . 

为什么Sum / aggregate列以字符串forms返回,而不是与总计数据库列相同的数据类型? 我想避免在任何地方洒上.to_s.to_f以解决这个问题。 有任何想法吗 ?

我想在这个彩虹的尽头得到的是这样的现金流列表 – (对于指定的日期范围……上面列出的查询的附加条件)。

  cat Type Heading TotalAmount | Operating | ExpCatX001 | 4000 | | | ExpCatX002 | 200 | | Financing | ExpCatX003 | 1000 | | Investing | ExpCatX004 | 4234 | | | ExpCatX005 | 201 | 

对于基本上需要整个自定义SQL语句的自定义查询(上面的查找并不完全抽象出来)我想设置一个代表新信息的快速小新模型。 即

 class OperatingExpenseReportDatum attr_accessor :type, :heading, :total def initialize(row) # set values from row, like @total = row["total"].to_f end end 

然后在模型中编写一个辅助方法,如:

 class Expense < AR::Base ... def self.operating_expenses rows = connection.select_all "SQL STATEMENT HERE" rows.map { |row| OperatingExpenseReportDatum.new(row) } end end 

然后你的报告生成很好:

 #controller @expenses = Expense.operating_expenses #view <% @expenses.each do |expense| %> <%= expense.type %>: <%= expense.total %> <% end %> 

或类似的东西。 🙂

活动记录尝试返回根据查询结果构建的“费用”对象。 但是费用对象没有totalAmount字段,因此就ActiveRecord而言,这是未经请求的“奖励数据”。 它可以把它扔掉,但它会为你保留它,它可以做出最宽容的类型假设。

我的建议:

  • 不要这样做; 它不是Active记录的工作方式; 细节的总数属于任何卷起来的东西; 它本身不是一个细节对象。
  • 如果您必须将其视为详细信息,请为字段amount命名,以便ActiveRecord知道如何处理它。
  • 如果您必须将总计作为自己的字段,请在费用模型中添加一个访问者,如下所示:

     def total_amount totalAmount.to_f end 

    并按如下方式使用它:您的代码中的operatingExpenses[1].total_amount

因为使用:select Rails让你无法告诉它(或驱动程序)什么样的本机类型来绑定结果集的字段(一旦它被全部检索 – 尽管大多数并非所有驱动程序都会在内部知道列的内容SQL类型是) – 因此它全部记录为字符串,并且通常丢弃SQL类型信息。 这一直适用于所有领域。 您必须手动执行转换( to_f ),就像Rails的ActiveRecord.instantiate方法必须在内部使用普通的ActiveRecord.find而不使用:select:joins (并且必须填充非字符串属性。)

您可以通过查看数据库驱动程序的select_raw方法来确认这一点。

干杯,五

只需执行find_by_sql并在小数列上应用SUM

 operating_expenses = Expense.find_by_sql ['SELECT cat.activityType, cat.name heading, SUM(exp.amount) AS amount FROM expenses exp JOIN expense_categories cat ON exp.category_id = cat.id GROUP BY cat.id, cat.activityType, cat.name, exp.id ORDER BY cat.activityType, amount DESC'] 

在operating_expenses的’amount’列下,您将自动将总数转换为十进制。

注意 :cat.id和exp.id需要遵守SQL标准。