我正在尝试找出按特定家庭的订单在给定日期范围内计算销售产品的最佳方式。
这些是我的简化模型:
Order
placed_on OrderItem
order_id,product_id,amount Product
family_id Family
所以,现在给出一些日期,比如说d1
和d2
,我需要计算给定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}
…
]
我知道如何在整个订单中循环,但我认为应该有更好的方法。
答案 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