这是一个示例模型设置(准系统Rails 3.0.5):
class Post < ActiveRecord::Base
has_many :comments
end
class Comment < ActiveRecord::Base
belongs_to :post
belongs_to :user
end
class User < ActiveRecord::Base
has_many :comments
has_many :commented_posts, through: :comments, source: :post, uniq: true
end
现在以下工作正常:
ruby-1.9.2-p0 > user.commented_posts.count
SQL (0.2ms) SELECT COUNT(DISTINCT "posts".id) FROM "posts" INNER JOIN "comments" ON "posts".id = "comments".post_id WHERE (("comments".user_id = 1))
=> 1
但是添加条件使得活动记录'忘记'关于uniq: true
位:
ruby-1.9.2-p0 > user.commented_posts.where("posts.id != 42").count
SQL (0.2ms) SELECT COUNT(*) FROM "posts" INNER JOIN "comments" ON "posts".id = "comments".post_id WHERE (("comments".user_id = 1)) AND (posts.id != 42)
=> 2
错误?或者我错过了什么?
修改
all
有效:
ruby-1.9.2-p0 > user.commented_posts.where("posts.id != 42").all
Post Load (0.3ms) SELECT DISTINCT "posts".* FROM "posts" INNER JOIN "comments" ON "posts".id = "comments".post_id WHERE (("comments".user_id = 1)) AND (posts.id != 42)
=> [#<Post id: 1, created_at: "2011-03-07 12:17:30", updated_at: "2011-03-07 12:17:30">]
显式uniq
:
ruby-1.9.2-p0 > user.commented_posts.where("posts.id != 42").uniq.count
Post Load (0.2ms) SELECT DISTINCT "posts".* FROM "posts" INNER JOIN "comments" ON "posts".id = "comments".post_id WHERE (("comments".user_id = 1)) AND (posts.id != 42)
=> 1
编辑2
确实是Rails中的错误。我提交了一个补丁。请进行投票,以便更快通过。 https://github.com/rails/rails/pull/2924#issuecomment-3317185
答案 0 :(得分:1)
我在3.0.9和3.0.10上都遇到了这个问题。我认为这是一个错误的bug,尽管可能有某些原因导致它的行为方式。
我尝试重写了连接方法的计数...
has_many :commented_posts, through: :comments, source: :post, uniq: true do
def count(column_name = nil, opts = {})
super(column_name || 'users.id', opts.reverse_merge(distinct: true))
end
end
但是如果存在条件,则arel会忽略count方法。这就是为什么我认为这是一个错误。
作为黑客攻击解决方案,我使用@ comments.count('users.id',distinct:true)强制arel在@comments可能在控制器中附加条件的情况下运行。
答案 1 :(得分:0)
我认为显式uniq导致计数从内存中的数组发生,而不是在数据库中进行计数。您可以从SELECT DISTINCT而不是SELECT COUNT DISTINCT中判断这是真的。这会将数据库中的所有条目拉入轨道,然后计算它们。
可以通过将几个参数传递给count
来让DB完成此操作collection.count(:id,:distinct =&gt; true)将导致生成正确的SQL代码。