【问题标题】:Active Record querying with joins and group by使用联接和分组依据的 Active Record 查询
【发布时间】:2021-10-25 12:23:10
【问题描述】:

我正在设计一个 API 来从以下场景中获取数据:

brands 表:

+------------+--------------+------+-----+---------+----------------+
| Field      | Type         | Null | Key | Default | Extra          |
+------------+--------------+------+-----+---------+----------------+
| id         | bigint(20)   | NO   | PRI | NULL    | auto_increment |
| name       | varchar(255) | YES  |     | NULL    |                |
+------------+--------------+------+-----+---------+----------------+

items 表:

+---------------------------+--------------+------+-----+---------+----------------+
| Field                     | Type         | Null | Key | Default | Extra          |
+---------------------------+--------------+------+-----+---------+----------------+
| id                        | bigint(20)   | NO   | PRI | NULL    | auto_increment |
| category_id               | bigint(20)   | YES  | MUL | NULL    |                |
| brand_id                  | bigint(20)   | YES  |     | NULL    |                |
+---------------------------+--------------+------+-----+---------+----------------+

item_skus 表:

+---------------------------+--------------+------+-----+---------+----------------+
| Field                     | Type         | Null | Key | Default | Extra          |
+---------------------------+--------------+------+-----+---------+----------------+
| id                        | bigint(20)   | NO   | PRI | NULL    | auto_increment |
| item_id                   | bigint(20)   | YES  | MUL | NULL    |                |
| number_of_stock           | int(11)      | YES  |     | NULL    |                |
+---------------------------+--------------+------+-----+---------+----------------+

ItemItemSkuBrand 的模型关联

  belongs_to :brand
  has_many :skus, class_name: 'ItemSku'

我只想了解每个品牌的库存可用商品和所有商品的数量。

{
  "brandCounts":[
    {
       "id":7006,
       "name":"Brand 01",
       "stockAvailableItemCount":50,
       "allItemCount":60
    },
    {
       "id":20197,
       "name":"Brand 02"
       "availableItemCount":150,
       "allItemCount":660
    }
  ]
}

实施:

brand_counts = []
brand_counts_hash = Hash.new()
items = Item.left_outer_joins(:skus).where(category_id: params[:id]).pluck(:brand_id, :number_of_stock, :item_id)

items.each do |item|
  brand_id = item[0]
  stock = item[1]

  if brand_counts_hash.has_key?(brand_id)
    item_count_arry = brand_counts_hash[brand_id]
    stock_available_item_count = item_count_arry[0]
    all_item_count = item_count_arry[1]
    if stock > 0
      brand_counts_hash[brand_id] = [stock_available_item_count + 1, all_item_count + 1]
    else
      brand_counts_hash[brand_id] = [stock_available_item_count, all_item_count + 1]
    end
  else
    stock_available_item_count = 0
    all_item_count = 0
    if stock > 0
      stock_available_item_count += 1
      all_item_count += 1
      brand_counts_hash[brand_id] = [stock_available_item_count, all_item_count]
    else
      all_item_count += 1
      brand_counts_hash[brand_id] = [stock_available_item_count, all_item_count]
    end
  end
end

brand_counts_hash.each do |key, value|
  stock_available_item_count = value[0]
  all_item_count = value[1]
  brand_counts << {
    id: key,
    name: get_brand_name(key),
    stock_available_item_count: stock_available_item_count,
    all_item_count: all_item_count
  }
  end
  @brand_counts = brand_counts
  render 'brands/counts/index', formats: :json
end

def get_brand_name(brand_id)
  brand = Brand.find_by(id: brand_id)
  brand.name unless brand == nil
end

有没有办法在没有多个循环的情况下进一步优化?

【问题讨论】:

标签: ruby-on-rails activerecord


【解决方案1】:

假设您的 Brand 模型还定义了以下关联

has_many :items

你想要的最终结果是这样的

{
  "brandCounts":[
    {
       "id":7006,
       "name":"Brand 01",
       "stockAvailableItemCount":50,
       "allItemCount":60
    },
    {
       "id":20197,
       "name":"Brand 02"
       "availableItemCount":150,
       "allItemCount":660
    }
  ]
}

当您复制并粘贴到您的项目时,以下代码可能不起作用。但它展示了如何用更少的代码解决这个问题

Brand.includes(items: :skus).all.map do |brand|
  {
    id: brand.id,
    name: brand.name,
    stockAvailableItemCount: brand.items.count,
    allItemCount: brand.items.map {|item| item.skus.sum(:number_of_stock)}.sum
  }
end

如果您需要json格式,只需将to_json用于上述代码的结果。

【讨论】:

  • brand.items.count 将导致 N+1 查询对项目进行计数。使用.size 或从数据库中选择聚合。
猜你喜欢
  • 1970-01-01
  • 2018-03-13
  • 1970-01-01
  • 1970-01-01
  • 2020-05-16
  • 2013-02-27
  • 1970-01-01
  • 2011-10-22
  • 2018-01-01
相关资源
最近更新 更多