首页 > 解决方案 > Rails:查询子孙,对子运行方法,对孙子的SQL SUM - 导致数组?

问题描述

楷模:

class Category < ApplicationRecord
  has_many :inventories
  has_many :stocks, through: :inventories
end

class Inventory < ApplicationRecord
  belongs_to :category
  has_many :stocks
end

class Stock < ApplicationRecord
  belongs_to :inventory
end

目标:

实现高效的 ActiveRecord 查询,该查询构建如下数组:

[
   { name: "Supplies", count: 10.00, total_value: 40.00 },
   { name: "Materials", count: 25.00, total_value: 30.00 }
]

名称->只是库存模型中的常规属性

count -> 股票表中 :count 列上的 SQL SUM

total_value -> 来自 Inventory 模型中进行一些数学运算的方法

这可能是一个完全的幻想,但我有一个很大的数据集,所以我试图让这个超高效。有任何想法吗?


编辑以回答问题:

total_value 是 Inventory 上的一个方法,然后调用 Stock 上的一个方法的总和:

def total_value
  stocks.map do |stock|
    stock.total_cost
  end.sum
end

total_cost 是 Stock 上的一种方法:

def total_cost
  cost_per_unit * count
end

标签: sqlruby-on-railsactiverecordparentchildren

解决方案


干得好:query = Inventory.group(:id, :name).select(:id, :name).left_joins(:stocks).select("SUM(stocks.count) AS count").select("SUM(stocks.cost_per_unit * stocks.count) AS total_value")

query.as_json给你正在寻找的东西。

您还可以通过以下方式访问数据find_eachquery.find_each { |record| puts "record #{record.name} has a total value of #{record.total_value}" }

如果要避免重复total_valueSQL 中的逻辑,则必须加载股票记录,如果有很多,这会大大减慢计算速度:

升级模型

class Inventory < ApplicationRecord
  def stocks_count
    stocks.sum(&:count)
  end

  def total_value
    stocks.sum(&:total_cost)
  end
end

和查询

Inventory.preload(:stocks).map do |inventory|
  {
    name: inventory.name, 
    count: inventory.stocks_count, 
    total_value: inventory.total_value
  }
end

如果您想将查询优化到 max,您可以考虑缓存 2 列total_valuestocks_countinventories表上。每次其库存发生变化(创建、删除、更新)时,您都会更新它们。它更难维护,但这是最快的选择。


推荐阅读