按计划按日期计算特定家庭销售的产品

时间:2018-04-20 09:16:00

标签: ruby-on-rails activerecord

我正在尝试找出按特定家庭的订单在给定日期范围内计算销售产品的最佳方式。

这些是我的简化模型:

  • Order placed_on
  • OrderItem order_id,product_id,amount
  • Product family_id
  • Family

所以,现在给出一些日期,比如说d1d2,我需要计算给定Product的{​​{1}}个Family在那些Order

所需的输出将是这样的:

# all these are products from the same family sold in the last week
[
  {"product_24": 3435},
  {"product_34": 566},
  {"product_83": 422}
  …
]

我知道如何在整个订单中循环,但我认为应该有更好的方法。

3 个答案:

答案 0 :(得分:3)

假设您的数据模型和变量应该类似于:

OrderItem.joins(:order)
         .joins(product: :family)
         .where(orders: {created_at: d1..d2})
         .where(products: {family_id: <YOUR_FAMILY_ID>})
         .group(:product_id)
         .sum(:amount)

这将生成以下sql:

SELECT
    SUM("order_items"."amount") AS sum_amount,
    "order_items"."product_id" AS order_items_product_id
FROM "order_items"
    INNER JOIN "orders" ON "orders"."id" = "order_items"."order_id"
    INNER JOIN "products" ON "products"."id" = "order_items"."product_id" 
    INNER JOIN "families" ON "families"."id" = "products"."family_id" 
WHERE
    ("orders"."created_at" BETWEEN ? AND ?)
        AND "products"."family_id" = ?
GROUP BY "order_items"."product_id"

并返回以下结构:

=> [{product_id => <sum of this product id since d1 until d2 for family_id>}, ...]

此外,我假设您想要总结每种产品的数量。让我知道是否适合你。

答案 1 :(得分:1)

class CreateOrders < ActiveRecord::Migration[5.1]
  def change
    create_table :orders do |t|
      t.timestamps
    end
  end
end

class CreateOrderItems < ActiveRecord::Migration[5.1]
  def change
    create_table :order_items do |t|
      t.integer :order_id, index:true
      t.integer :product_id, index:true
      t.integer :amount
      t.timestamps
    end
  end
end

class CreateProducts < ActiveRecord::Migration[5.1]
  def change
    create_table :products do |t|
      t.text :name
      t.integer :family_id, index:true
      t.timestamps
    end
  end
end

class CreateFamilies < ActiveRecord::Migration[5.1]
  def change
    create_table :families do |t|
      t.text :name
      t.timestamps
    end
  end
end

class Family < ApplicationRecord
  has_many :products
end

class Order < ApplicationRecord
  has_many :order_items
  has_many :products, through: :order_items
end

class OrderItem < ApplicationRecord
  belongs_to :order
  belongs_to :product
end

class Product < ApplicationRecord
  belongs_to :family
  has_many :order_items
end

irb(main):015:0> Order.joins(:order_items).joins(:products).where("products.family_id":2).where("orders.created_at": [(Time.now).to_date..(Time.now + 1.day).to_date]).count
   (0.6ms)  SELECT COUNT(*) FROM "orders" INNER JOIN "order_items" ON "order_items"."order_id" = "orders"."id" INNER JOIN "order_items" "order_items_orders_join" ON "order_items_orders_join"."order_id" = "orders"."id" INNER JOIN "products" ON "products"."id" = "order_items_orders_join"."product_id" WHERE "products"."family_id" = ? AND ("orders"."created_at" BETWEEN '2018-04-20' AND '2018-04-21')  [["family_id", 2]]
=> 3

答案 2 :(得分:1)

不是完整的答案,因为你已经有两个相当详细的答案了 - 但是有些说明

我设置了范围...因为我们不确定您的数据库是否垂直......同时,请避免任何时间范围问题,确保您在将所有内容输入之前获得整整一天的时间查询。这些只是示例而非语法检查或推荐的最佳表单

  # ensure we are getting whole of each day
  full_d1 = d1.beginning_of_day
  full_d2 = d2.end_of_day

  #  change the order based on whatever you have more of
  scope :orders -> {where(created_at: full_d1..full_d2)}
  scope :family -> {where(product: family_id)}

  # use something like - swap order based on db conditions
  Order.orders.family  # ... then add the rest of what they are throwing down or .size / .count

此外,如S / O中的其他地方所述,您可以缓存has_many关系的计数 - link