【发布时间】:2023-03-04 06:56:01
【问题描述】:
我有一个 Comments 表和一个 Posts 表,其中一个 Post 有许多 cmets,一个评论属于一个帖子(即它在表中有一个 post_id)。
如何有效地选择最后十个没有 cmets 的帖子。 如果不先选择所有帖子并检查每个帖子的评论数为 0,我似乎无法完成此操作。
提前感谢您的帮助。
-内森
【问题讨论】:
标签: ruby-on-rails activerecord model
我有一个 Comments 表和一个 Posts 表,其中一个 Post 有许多 cmets,一个评论属于一个帖子(即它在表中有一个 post_id)。
如何有效地选择最后十个没有 cmets 的帖子。 如果不先选择所有帖子并检查每个帖子的评论数为 0,我似乎无法完成此操作。
提前感谢您的帮助。
-内森
【问题讨论】:
标签: ruby-on-rails activerecord model
你可以的
Post.find(:all, :include => :comments, :conditions => "comments.id is null", :limit => 10, :order => "created_at desc")
【讨论】:
您可以将counter cache 添加到Comment 模型,并将几个named scopes 添加到Post 模型。比如:
class Post < ActiveRecord::Base
has_many :comments
named_scope :recent, :limit => 10, :order => 'created_at DESC'
named_scope :uncommented, :conditions => { :comments_count => 0 }
end
class Comment < ActiveRecord::Base
belongs_to :post, :counter_cache => true
end
如果您使用的是 Rails 3,那么命名范围语法会有些不同:
class Post < ActiveRecord::Base
has_many :comments
scope :recent, limit(10).order('posts.created_at DESC')
scope :uncommented, where(:comments_count => 0)
end
现在您可以通过将命名范围链接在一起来查找没有任何 cmets 的帖子:
@uncommented = Post.recent.uncommented
如果您想获取所有没有任何 cmets 的帖子(即不仅仅是最近的十个帖子),那么它会是:
@uncommented = Post.uncommented
——您可能已经注意到,在 Rails 3 示例中,我将 posts 表名包含在 :recent 范围内。这是一个很好的实践,如果两个表具有相同的列名,您可以避免 SQL 查询中的列名不明确。 This Railscast 解释更多。
【讨论】:
在您的帖子表中添加一个comments_count 列,并让迁移更新计数器。从现在开始,如果您在关联中提供:counter_cache => true,Rails 将自动更新您的计数器。现在很容易选择最后 10 个帖子:
Post.find_all_by_comments_count 0, :order => 'created_at DESC', :limit => 10
文档链接:http://rails.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#M001318
【讨论】:
你必须像这样添加一个条件:
:conditions => '(SELECT count(*) FROM comments WHERE post_id = #{id}) = 0'
我不是 SQL 专家,所以可能有类似 EMPTY 关键字之类的东西。
【讨论】:
你需要的是左外连接:
Post.all :joins => "left outer join comments on posts.id = comments.post_id", :conditions => "comments.id is null", :limit => 10, :order => 'created at desc'
此链接应该可以帮助您了解不同类型的 SQL 连接:http://www.codinghorror.com/blog/2007/10/a-visual-explanation-of-sql-joins.html
【讨论】:
Post.includes(:cmets).where("cmets.id is null")
【讨论】: