另一个答案的替代方案最终会为您提供相同的结果集,但副作用会略有不同:
Concert.select("concerts.*, count(reviews.id) review_count")
.joins("LEFT JOIN reviews ON concerts.id = reviews.concert_id")
.group("concerts.id")
.order("review_count")
主要区别是这个查询在使用之前不会立即执行;您将收到一个活动记录关系,就像您通常在使用任何活动记录查询方法时一样,这意味着您可以进一步细化或添加到查询中(假设)。
另一个区别是这个查询不需要预先加载reviews。根据您的需要,如果您不需要相关评论中的任何信息,此查询将运行得更快。
就时间/性能而言,使用包含 50 场音乐会和 43867 条评论的数据库(索引存在于 FK 上),两个版本似乎几乎在同一时间执行(预加载)。这是我的基准测试结果表(所有结果都以秒为单位):
| # | Sory_by Query | Pure AR Query | No eager loading |
--------------------------------------------------------
| 1 | 2.899806 | 2.840702 | 0.02164 |
| 2 | 3.150556 | 2.818374 | 0.21612 |
| 3 | 2.812867 | 3.025921 | 0.016368 |
| 4 | 3.069562 | 3.055307 | 0.016884 |
| 5 | 2.862722 | 2.680357 | 0.021316 |
|---|---------------|---------------|------------------|
AVG: 2.9591026 | 2.8841322 | 0.0584836 |
如您所见,使用预先加载的两个查询之间没有显着差异,而使用我上面的查询而不使用预先加载的主要区别。显然,您的确切结果会有所不同,但这应该让您了解相对差异。
旁注:
从您在问题中发布的内容来看,您似乎/想要在 ERB 视图中编写此查询。我强烈建议将此查询移动到Concert 模型中的一个方法,并从控制器中该方法的返回创建一个实例变量,然后视图可以使用该实例变量。这样你就可以保持一切都很好并且分开。
编辑
为了说明我的建议,我会将查询放在 Concert 模型的类方法中,如下所示:
def self.ordered_by_reviews
return select("concerts.*, count(reviews.id) review_count")
.joins("LEFT JOIN reviews ON concerts.id = reviews.concert_id")
.group("concerts.id")
.order("review_count")
end
你可以从你的控制器调用哪个(不管哪个控制器):
... other controller code:
@concerts = Concert.ordered_by_reviews
... and so on
然后您可以根据需要使用@concerts,并可以删除@concerts = Concert.all 等任何内容。
或者,您也可以使用作用域来做同样的事情(我相信无论如何都会被认为是更多的 Rails-y):
class Concert < ActiveRecord::Base
scope :ordered_by_review, -> { select("concerts.*, count(reviews.id) review_count").joins("LEFT JOIN reviews ON concerts.id = reviews.concert_id").group("concerts.id").order("review_count") }
... rest of class