我有两个型号
class Product < ActiveRecord::Base
belongs_to :category
end
class Product < ActiveRecord::Base
belongs_to :category
end
现在我想用最便宜的产品列出所有类别。通过获取所有类别,迭代它们并单独找到最便宜的产品,这很容易完成,但这会进行大量查询并且速度非常慢。我可以用sql很容易地做到这一点 - 比如
SELECT products.*, categories.*
FROM products
JOIN categories ON (categories.id = products.owner_id)
LEFT JOIN products as cheaper_products ON
cheaper_products.category_id = epochs.category_id AND
cheaper_products.price < products.price
WHERE cheaper_products.owner_id IS NULL
这是一个很好的技巧,我们“LEFT JOIN”所有类别中的所有更便宜的产品到每个产品,而不仅仅是那些没有任何产品。
我想知道使用Rails3关系可以做些类似事情 - 我正在使用squeel,因此它也可以使用。
观察:我想过定义一个关系:产品上的cheaper_products,但它似乎也无济于事。
另一个想法:也可以用子查询来解决它,返回其类别中所有最便宜产品的ID,但它也不让我解决(并且不太优雅)。
注意:我知道如何使用bruteforce(selector_sql),但我真的想学习更多rails 3方法。
答案 0 :(得分:2)
这是一个有趣的问题,我认为你走的是正确的道路。您可以使用纯SQL查询更轻松地解决它,然后处理ActiveRecord,因为您无法避免以任何方式编写SQL。
一种方法是在连接中将预先加载与SQL结合起来,这样只执行一个查询:
# app/models/category.rb
class Category < ActiveRecord::Base
has_many :products
end
# app/models/product.rb
class Product < ActiveRecord::Base
belongs_to :category
def self.cheapest
joins(:category, "LEFT OUTER JOIN products AS cheaper_products ON (products.category_id = cheaper_products.category_id AND cheaper_products.price < products.price)").
where("cheaper_products.category_id IS ?", nil).
includes(:category)
end
end
在控制台中测试时
1.9.3-p125 :001 > Product.cheapest.to_sql
=> "SELECT \"products\".* FROM \"products\" INNER JOIN \"categories\" ON \"categories\".\"id\" = \"products\".\"category_id\" LEFT OUTER JOIN products\n AS cheaper_products\n ON (products.category_id = cheaper_products.category_id\n AND cheaper_products.price < products.price) WHERE (cheaper_products.category_id IS NULL)"
1.9.3-p125 :002 > Product.where(:category_id => 1).minimum(:price)
(0.2ms) SELECT MIN("products"."price") AS min_id FROM "products" WHERE "products"."category_id" = 1
=> 16.0
1.9.3-p125 :003 > Product.where(:category_id => 2).minimum(:price)
(0.3ms) SELECT MIN("products"."price") AS min_id FROM "products" WHERE "products"."category_id" = 2
=> 7.0
1.9.3-p125 :004 > Product.where(:category_id => 3).minimum(:price)
(0.3ms) SELECT MIN("products"."price") AS min_id FROM "products" WHERE "products"."category_id" = 3
=> 19.0
1.9.3-p125 :005 > cheap_products = Product.cheapest
...
1.9.3-p125 :006 > cheap_products.each {|product| p [product.price, product.category.name] }
[16.0, "Category 0"]
[7.0, "Category 1"]
[19.0, "Category 2"]
答案 1 :(得分:0)
你可以试试这个:
categories = Category.includes(:products).group('categories.id,products.id').having('MIN(products.price)')
categories.each {|c| p c.products.first} # List of cheapest product in each category
这不包括没有产品的类别。