Rails has_many 关联计数子行

Posted

技术标签:

【中文标题】Rails has_many 关联计数子行【英文标题】:Rails has_many association count child rows 【发布时间】:2010-10-22 05:19:33 【问题描述】:

什么是“rails 方式”可以有效地获取父表的所有行以及每行具有的子表的数量?

我不想使用counter_cache,因为我想根据某些时间条件运行这些计数。

陈词滥调的博客示例: 文章表。每篇文章有 0 个或多个 cmets。

我希望能够提取过去一小时、一天、一周内每篇文章有多少个 cmets。

但是,理想情况下,我不想遍历列表并对每篇文章进行单独的 sql 调用,也不想使用 :include 预取所有数据并在应用服务器上处理。

我想运行一条 SQL 语句并获得一个包含所有信息的结果集。

我知道我可以硬编码出完整的 SQL,也许可以使用 .find 并设置 :joins:group:conditions 参数...但我想知道是否有“更好”的方式...又名“Rails 方式”

【问题讨论】:

这是another的答案。 【参考方案1】:

从 SQL 的角度来看,这看起来很简单 - 只需编写一个新查询即可。

从 Rails 的角度来看,您提到的值是计算值。因此,如果您使用find_by_sql,Model 类将不知道计算字段,因此即使您设法将查询转换为 Rails 语言,也会将计算值作为字符串返回。请参阅下面的链接问题。 一般的偏差(根据我对那个问题的回答)是让一个单独的类负责汇总/计算所需的值。

How to get rails to return SUM(columnName) attributes with right datatype instead of a string?

【讨论】:

【参考方案2】:

这个 activerecord 调用应该做你想做的事:

Article.find(:all, :select => 'articles.*, count(posts.id) as post_count',
             :joins => 'left outer join posts on posts.article_id = articles.id',
             :group => 'articles.id'
            )

这将返回文章对象的列表,每个对象都有 post_count 方法,其中包含文章上的帖子数作为字符串。

方法执行sql类似如下:

SELECT articles.*, count(posts.id) AS post_count
FROM `articles`
LEFT OUTER JOIN posts ON posts.article_id = articles.id
GROUP BY articles.id

如果您好奇,这是您在运行此类查询时可能看到的 mysql 结果示例:

+----+----------------+------------+
| id | text           | post_count |
+----+----------------+------------+
|  1 | TEXT TEXT TEXT |          1 |
|  2 | TEXT TEXT TEXT |          3 |
|  3 | TEXT TEXT TEXT |          0 |
+----+----------------+------------+

【讨论】:

对于那些使用 PostgreSQL 的用户的快速说明,您需要为选择和组提供所有检索到的列。 在 Rails 3 中,这将是 Article.select('foo').joins('bar').group('baz') 等。 @JaredBeck,您的行导致 Rails 对表执行 INNER JOIN,这不会返回任何具有 0 个关联 Post 行的文章行。 也许我应该说select('foo').joins('left join bar')。我只是想展示新的arel 优点。【参考方案3】:

Rails 3 版本

对于 Rails 3,您会看到如下内容:

Article.select("articles.*, count(comments.id) AS comments_count")
  .joins("LEFT OUTER JOIN comments ON comments.article_id = articles.id")
  .group("articles.id")

感谢 Gdeglin 提供 Rails 2 版本。

Rails 5 版本

因为 Rails 5 有 left_outer_joins 所以你可以简化为:

Article.select("articles.*, count(comments.id) AS comments_count")
  .left_outer_joins(:comments)
  .group("articles.id")

因为您问的是 Rails 方式:没有办法使用 ActiveRecord 来简化/更进一步地简化/railsify。

【讨论】:

【参考方案4】:

我用来解决这个问题的一个简单方法是

在我的模型中我做了:

class Article < ActiveRecord::Base
  has_many :posts

  def count_posts
    Post.where(:article_id => self.id).count
  end
end

现在,您可以使用例如:

Articles.first.count_posts

我不确定它是否可以更有效,但它是一种解决方案,在我看来比其他解决方案更优雅。

【讨论】:

当从文章/索引路由中检索文章时,这将为返回的每篇文章触发一个计数查询。如果每个计数保守地说是 0.25 毫秒,而您要求 100 篇文章,那么请求会增加 25 毫秒。因此,如果在您添加它之前请求是 5 毫秒,那么现在它是 30 毫秒 - 慢了 6 倍。优雅但不高效。 正如我所说! 你确实做到了——但我只是通过粗略的计算来说明它是多么低效,因此后续用户可以做出明智的选择:)【参考方案5】:

我是这样完成的:

def show
  section = Section.find(params[:id])
  students = Student.where( :section_id => section.id ).count
  render json: status: 'SUCCESS', section: students,status: :ok
end

在此我有 2 个模型部分和学生。所以我必须计算与特定部分 id 匹配的学生人数。

【讨论】:

以上是关于Rails has_many 关联计数子行的主要内容,如果未能解决你的问题,请参考以下文章

Rails has_many,如何实现嵌套关联?

Rails:为啥“has_many ...,:通过=> ...”关联导致“NameError:未初始化的常量...”

Rails:has_many 通过关联 - 我做对了吗?

在 Rails 中,如何在一个查找器中查询两个 has_many 关联?

如何将参数传递给 Rails 4 中的 has_many 关联范围?

Rails ActiveRecord 关联“has_many,每个都有 has_one”