我在其中有查询的代码中输出了结果(此处仅显示基本内容) 因此,基本上,我需要自定义订单项以及所有订单项的总和
results = Order.includes(:customer, :line_items).where('completed_at IS NOT NULL')
results.each do |result|
custom_items_sum = result.line_items.where(line_item_type: 'custom').sum(:amount)
total_sum = result.line_items.sum(:amount)
end
在此代码中,存在n + 1个查询问题,我尝试添加包含,但由于在循环中有另一个查询,因此确保它无法正常工作,我们将不胜感激。
答案 0 :(得分:3)
如果您不想在循环中触发其他查询,则需要避免对关系起作用的方法,而应使用对集合起作用的方法。试试
custom_items_sum = result.line_items.
select { |line_item| line_item.line_item_type == 'custom' }.
sum(&:amount)
这应该在没有n + 1个查询的情况下起作用。
请注意,有可能只编写一个查询,无论如何避免这种计算,但这超出了您的问题范围:)
答案 1 :(得分:2)
Rails从未像ORM那样强大。改用普通SQL:
results =
Order.connection.execute <<-SQL
SELECT order.id, SUM(line_items.amount)
FROM orders
JOIN line_items
ON (line_items.order_id = orders.id)
WHERE orders.completed_at IS NOT NULL
GROUP BY orders.id
HAVING line_items.line_item_type = 'custom'
SQL
这样,您将在一个查询中获得所有中间和,这比在ruby中执行所有计算要快得多。
答案 2 :(得分:1)
尝试使用作用域块。以下代码生成非常干净的SQL查询。
Order.includes(:line_items).where.not(completed_at: nil).scoping do
@custom_items_sum = Order.where(line_items: { line_item_type: 'custom' })
.sum(:amount)
@total_sum = Order.sum(:amount)
end
关于scoping
块的文档并不多,但是它将您的模型的范围限定为之前发出的ActiveRecord请求(此处为where('completed IS NOT NULL')
,并附带了:line_items
)。
希望这会有所帮助! :)
答案 3 :(得分:1)
只是因为@AlekseiMatiushkin说用原始SQL编写它,所以我们对rails做同样的事情
order_table = Order.arel_table
line_items_table = LineItem.arel_table
custom_items = Arel::Table.new(:custom_items)
Order.select(
order_table[Arel.star],
line_items_table[:amount].sum.as('total_sum'),
custom_items[:amount].sum.as('custom_items_sum')
).joins(
order_table.join(line_items_table).on(
line_items_table[:order_id].eq(order_table[:id])
).join(
Arel::Nodes::As.new(line_items_table,:custom_items),
Arel::Nodes::OuterJoin
).on(
custom_items[:order_id].eq(order_table[:id]).and(
custom_items[:line_item_type].eq('custom')
)
).join_sources
).where(
order_table[:completed_at].not_eq(nil)
).group(:id)
这将使用以下查询生成ActiveRecord::Relation
个对象,其中Order
个对象的虚拟属性分别为total_sum
和custom_items_sum
SELECT
orders.*,
SUM(line_items.amount) AS total_sum,
SUM(custom_items.amount) As custom_items_sum
FROM
orders
INNER JOIN line_items ON line_items.order_id = orders.id
LEFT OUTER JOIN line_items AS custom_items ON custom_items.order_id = orders.id
AND custom_items.line_item_type = 'custom'
WHERE
orders.completed_at IS NOT NULL
GROUP BY
orders.id
这应该通过使用2个联接来聚合所需的数据,从而在单个查询中处理请求。