Rails / Postgres:列必须出现在GROUP BY子句中

时间:2011-08-03 18:35:24

标签: ruby-on-rails postgresql

我有一个将用户加入帖子的范围,以便只获得具有可见帖子的用户。这适用于MySQL,但PG更严格,并且会抛出错误。

用户模型:

belongs_to :account

scope :have_posts, joins(:posts).where('posts.visible => true').group('users.id')

控制器:

@account.users.have_posts.each do |user|
  # do stuff
end

错误:

  

(PGError:错误:列“users.account_id”必须出现在GROUP BY子句中或用于聚合函数:SELECT“users”。* FROM“users”INNER JOIN“posts”ON“posts”。“ user_id“=”users“。”id“WHERE(”users“.account_id = 1)AND(recommendations.approved = true)GROUP BY users.id)

它抱怨来自调用@account.users的“users.account_id”(因为我显然不希望数据库中的所有用户)。

知道如何解决?

2 个答案:

答案 0 :(得分:6)

问题是GROUP BY子句。如果您使用此功能,则无法选择任何非聚合字段,因此SELECT "users".* [...]不起作用。来自Postgres文档:

  

通常,如果表被分组,则未使用的列   除聚合表达式外,不能引用分组。

这样的事可能有用,虽然很乱:

scope :have_posts, 
  joins('inner join (select user_id from posts where visible = true group by user_id) users_with_posts on users_with_posts.user_id=users.id')

一种替代方法是使用MAXMIN等聚合函数指定每个选定的字段,但这可能会使范围更长,功能更少。

答案 1 :(得分:0)

这应表示为where条件而不是joins,因为不需要来自posts的任何数据。恕我直言更容易阅读:

scope :have_posts, -> {
  where(
    'EXISTS (SELECT 1 FROM posts p WHERE p.id = users.id AND visible = true)'
  )
}

Ruby 2和Rails 3/4准备就绪。 PG优化器会尽快运行它。