【问题标题】:How to turn queries to perform efficiently in rails?如何将查询转换为在 Rails 中高效执行?
【发布时间】:2013-05-14 19:31:51
【问题描述】:

假设我有 15 个类别和 6 个子类别,并且我有表项,其中我有一组记录,我必须按以下方式获取这些记录

category 1 ---> level 1 ---> 3 items with maximum price
category 1 ---> level 2 ---> 3 items with maximum price
  ...
  ...
  ...
category 15 ---> level 6 ---> 3 items with maximum price

@categories.each do |value|
   @sub-categories.each do |value1|
      array = Item.find(:all, :conditions => ["customer_id IN (?) AND category_id = ? AND sub-category_id = ?", @customer, value.id, value1.id], :order => 'price DESC', :limit => 3)
            array.each do |value2|
                   @max_price_item_of_each_customer << value2
            end
          end
        end

但这将花费大量时间,因为它会迭代。那么我怎样才能以这样的方式改变这一点,可以减少时间呢?任何帮助表示赞赏。

【问题讨论】:

  • 你能举一个具体的例子来说明类别、级别和项目可能是什么吗?我很难理解你的数据结构。

标签: ruby-on-rails ruby-on-rails-3 performance query-optimization


【解决方案1】:

这一切都取决于您使用的记录的规模,但如果您使用的是合理的集合,这应该会更快,并将您的查询减少到 1。

@customer_id = 1
@categories  = [1, 2, 3]
@subs        = [4, 5, 6]

@max_price_item_of_each_customer = []
items = Item.where(customer_id: @customer, category_id: @categories, subcategory_id: @subcategories)
items.group_by{|item| item.category_id}.each_pair do |category_id, category_items|
  category_items.group_by{|item| item.subcategory_id}.each_pair do |subcategory_id, subcategory_items|
    @max_price_item_of_each_customer += subcategory_items.sort{|x, y| y.price <=> x.price }.first(3)
  end
end

【讨论】:

  • 谢谢你,这对我有用,只需稍加修改。您在“@max_price_item_of_each_customer +”之后错过了等号,我尝试编辑但我不能,因为这是唯一的变化。
【解决方案2】:

以下查询对我有用

   @max_price_item_of_each_customer =Item.find_by_sql(["SELECT i1.* FROM item i1
      LEFT OUTER JOIN item i2 ON (i1.category_id = i2.category_id AND i1.sub-category_id = i2.sub-category_id AND i1.id < i2.id)
      WHERE i1.customer_id IN (?) AND i1.category_id IN (?)
      GROUP BY i1.id HAVING COUNT(*) < 3
      ORDER BY price DESC", @customer, @categories.map(&:id)])

【讨论】:

    【解决方案3】:

    如果您使用 Postgresql,下面的解决方案可能会起作用。

    1. items 表中选择一组3 个项目ID,按price 降序排序并按category_idsubcategory_id 分组。您可以使用 Postgres array_agg 来收集分组后的项目 id。
    2. 选择项目行,其中项目 ID 位于这些分组项目 ID 中。之后,按category_id 升序、subcategory_id 升序和price 降序对结果进行排序

    结果是ActiveRecord::Relation,因此您可以照常迭代项目。由于结果是扁平化的(但已经按类别、子类别和价格排序),因此您需要自己将不同的类别和子类别分开。

    grouped_item_ids = Item.where(customer_id: customer_id).
      select("items.category_id, items.subcategory_id, (array_agg(items.id order by items.price desc))[1:3] AS item_ids").
      group("items.category_id, items.subcategory_id").map {|item| item["item_ids"]}
    @items = Item.where(id: grouped_item_ids.flatten).
      order("items.category_id ASC, items.subcategory_id ASC, items.price desc")
    

    【讨论】:

      【解决方案4】:

      试试:

      @max_price_item_of_each_customer = []
      @categories.each do |value|   
            @max_price_item_of_each_customer +=  Item.find(:all, :conditions => ["customer_id IN (?) AND category_id = ? AND sub-category_id in (?)", @customer, value.id, @sub-categories.map(&:id)], :order => 'price DESC', :limit => 3)            
      end
      

      【讨论】:

      • 我会试试这个。但在此之前,您能否告诉我这将如何从每个子类别中获取 3 个项目。我认为这将为每个类别获取 3 个项目。
      • 我已经尝试过,它为每个类别而不是每个子类别选择 3 个项目。
      猜你喜欢
      • 2016-01-27
      • 1970-01-01
      • 1970-01-01
      • 2021-09-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多