4

I have a scope that joins users to posts, to get only users that have visible posts. This works in MySQL, but PG is a lot more strict, and throws an error.

User Model:

belongs_to :account

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

Controller:

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

Error:

(PGError: ERROR: column "users.account_id" must appear in the GROUP BY clause or be used in an aggregate function: 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)

It's complaining about "users.account_id" which is from calling @account.users (as I obviously don't want all users in the DB).

Any idea how to fix?

2 Answers 2

6

The problem is the GROUP BY clause. If you use this, you cannot select any non-aggregated fields, so SELECT "users".* [...] doesn't work. From the Postgres docs:

In general, if a table is grouped, columns that are not used in the grouping cannot be referenced except in aggregate expressions.

Something like this may work, although messy:

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')

One alternative would be to specify every selected field using aggregate functions like MAX or MIN, but this will probably make the scope even longer and less versatile.

Sign up to request clarification or add additional context in comments.

Comments

0

This should be expressed as where condition not joins, because there is no need for any data from posts. IMHO much easier to read:

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

Ruby 2 and Rails 3/4 ready. PG optimizer will take care to run it as fast as possible.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.