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 ...,:通过=> ...”关联导致“NameError:未初始化的常量...”
在 Rails 中,如何在一个查找器中查询两个 has_many 关联?