Rails: запрос детей и внуков, метод run для потомков, SQL SUM для внуков - в результате получается массив? - PullRequest
0 голосов
/ 08 сентября 2018

Модели:

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 }
]

name -> обычный атрибут в модели инвентаризации

count -> сумма SQL в столбце: count в таблице запасов

total_value -> из метода в модели инвентаризации, который выполняет некоторые математические операции

Это может быть полная фантазия, но у меня большой набор данных, поэтому я пытаюсь сделать этот гиперэффективный. Есть идеи?


Изменить для ответа на вопрос:

total_value - метод в Inventory, который затем вызывает сумму метода в Stock:

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

total_cost - это метод на складе:

def total_cost
  cost_per_unit * count
end

1 Ответ

0 голосов
/ 08 сентября 2018

Вот, пожалуйста, 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_each: query.find_each { |record| puts "record #{record.name} has a total value of #{record.total_value}" }

Если вы хотите избежать дублирования логики из total_value в SQL, у вас есть для загрузки записей об акциях, что значительно замедляет вычисления, если их много:

Обновление модели

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

Если вы хотите оптимизировать свой запрос до максимального значения , вы можете рассмотреть возможность кэширования 2 столбцов total_value и stocks_count в таблице inventories. Вы будете обновлять их каждый раз, когда изменяется одна из его акций (создание, удаление, обновление). Его сложнее поддерживать, но это самый быстрый вариант.

...