来自具有匹配特定条件的关联模型的模型的总和属性

Sum attribute from model that has associated models that match certain condition

在 Rails 项目的 Ruby 4.0 中,我有一个 Payments 模型 has_many Transactions 模型。

如果我有以下数据

SELECT * FROM payments;
id | paid_amount
---+--------------
1  | 200
2  | 300
3  | 100
4  | 400
5  | 100
6  | 600
7  | 100
8  | 100
9  | 800

SELECT * FROM transactions;
id | payment_id  | type
---+-------------+------
1  | 2           | cash
2  | 3           | credit
3  | 1           | credit
4  | 4           | cash
5  | 1           | cash
6  | 6           | credit
7  | 1           | cash
8  | 1           | credit
9  | 8           | cash

现在,我正在计算来自 Payments 的数字属性 paid_amount 的总和,该属性具有特定类型的交易,如下所示

> Payment.where(id: Transaction.where(type: 'cash').pluck(:payment_id)).sum(:paid_amount)
> 1000
> # The sum of paid_amount from payments with ids 1, 2, 4 and 8

但这对于成千上万条记录来说速度不够快,所以我尝试用 includes 完成此操作,但没有任何运气。

> Payment.includes(:transactions).where("transactions.type = 'cash'").sum(:paid_amount)
> 1200
> # paid_amount of payment with id 1 is considered two times because of transactions with id 5 and 7

关于我应该如何计算我需要的数量有什么想法吗?

使用 joinswhere 到 return 仅您想要的交易

joins documentation

Payment.joins(:transactions).where(transactions: {type:'cash'}).sum(:paid_amount)

您的第一个查询:

Payment.where(id: Transaction.where(type: 'cash').pluck(:payment_id)).sum(:paid_amount)

很慢,部分原因是 Transaction.where(type: 'cash').pluck(:payment_id) 是一个查询 returns 一个数组,然后您将该数组发送回数据库,并在 SQL 中使用 IN (big_list_of_integers)。稍作修改将使用子查询,只需将 pluck 切换为 select:

Payment.where(id: Transaction.where(type: 'cash').select(:payment_id)).sum(:paid_amount)

这将导致一个查询,如:

select sum(payments.paid_amount)
from payments
where payments.id in (
  select payment_id
  from transactions
  where transactions.type = ...
)

正在发送到数据库。对于大型列表,这应该会快很多。