【问题标题】:How to add sum of a field from joined model in Rails?如何在 Rails 中添加连接模型中的字段总和?
【发布时间】:2014-12-13 02:50:24
【问题描述】:

如何使用 Rails 3.2 和 MySql 5.5 从连接模型中添加字段的总和?

假设我有这样的模型:

class Account < ActiveRecord::Base
  attr_accessible :number
  has_many :operations
end

class Operation < ActiveRecord::Base
  belongs_to :account
  attr_accessible :op_type,  # either 'deposit' or 'withdrawal'
                  :amount
end

我需要使用某些条件选择账户,然后将账户所有存款的总和添加到每个账户中。

这可以通过这样的 SQL 来完成:

SELECT *,
    IFNULL((
        SELECT SUM(amount)
        FROM operations
        WHERE operations.account_id = accounts.id AND operations.op_type = 'deposit'
    ), 0) as total_deposits
FROM accounts
WHERE <condition for accounts>

(使用 LEFT JOIN 是实现相同结果的另一种方法。)

如何使用 Rails 做到这一点?

我想要这样的东西:

accounts = Account.where(<mycondition>). join(???). sum(???)  # What should be here?
accounts.each do |a|
  puts "Account #{a.number} has deposited #{a.total_deposits} total."
end

【问题讨论】:

    标签: mysql ruby-on-rails ruby-on-rails-3 ruby-on-rails-3.2


    【解决方案1】:

    试试Operation.joins(:account).where(&lt;mycondition&gt;).sum(:amount)

    被求和的字段amountoperations 表中;所以活动记录查询也将在Operation 模型上。 mycondition 应定义为包含属于特定帐户的操作。

    【讨论】:

    • Operation.joins(:account) 不起作用,因为它不返回还没有存款记录的账户。我需要结果集中的这些帐户。对于他们来说,total_deposits 应该是 0。
    【解决方案2】:

    如果您需要使用LEFT JOIN 来检索没有操作记录的帐户,您需要输入以下连接条件:

    totals = Account.where(<account conditions>).joins("LEFT JOIN operations ON operations.account_id = accounts.id AND operations.op_type = 'deposit'").group("accounts.number").sum(:amount)
    totals.each do |a|
      puts "Account #{a[0]} has deposited #{a[1]} total."
    end
    

    如果您愿意将其拆分为两个查询,这是一个选项:

    accounts = Account.where(<account conditions>)
    totals = Operation.where(op_type: "deposit", account_id: accounts.map(&:id)).group(:account_id).sum(:amount)
    accounts.each do |a|
      puts "Account #{a.number} has deposited #{totals[a.id] || 0} total."
    end
    

    编辑:如果您需要帐户实例并且需要按总和排序,则将开始出现一些额外的 SQL。但是这样的事情应该可以工作:

    accounts = Account.where(<account conditions>).joins("LEFT JOIN operations ON operations.account_id = accounts.id AND operations.op_type = 'deposit'").group("accounts.number").select("accounts.*, COALESCE(SUM(amount), 0) AS acct_total").order("acct_total")
    accounts.each do |a|
      puts "Account #{a.number} has deposited #{a.acct_total} total."
    end
    

    【讨论】:

    • 第一个解决方案生成我需要的 SQL 结果集,但它的行在 Ruby 中作为数组返回。是否可以改为获取 Account 实例?
    • 第二种解决方案不符合我的需求,因为我需要按total_deposits desc 对结果集进行排序。 (为简单起见,我省略了原始问题中的排序。)
    猜你喜欢
    • 2014-11-23
    • 2015-08-12
    • 1970-01-01
    • 2012-02-29
    • 2011-07-24
    • 1970-01-01
    • 1970-01-01
    • 2020-06-03
    • 2012-05-16
    相关资源
    最近更新 更多