Rails 3.1 с PostgreSQL: GROUP BY должен использоваться в агрегатной функции

Я пытаюсь загрузить последние 10 искусств, сгруппированных по user_id и упорядоченных created_at. Это отлично работает с SqlLite и MySQL, но дает ошибку в моей новой базе данных PostgreSQL.

Art.all(:order => "created_at desc", :limit => 10, :group => "user_id")

ошибка ActiveRecord:

Art Load (18.4ms)  SELECT "arts".* FROM "arts" GROUP BY user_id ORDER BY created_at desc LIMIT 10
ActiveRecord::StatementInvalid: PGError: ERROR:  column "arts.id" must appear in the GROUP BY clause or be used in an aggregate function
LINE 1: SELECT  "arts".* FROM "arts"  GROUP BY user_id ORDER BY crea...

какие идеи?

4 ответов


sql, генерируемый выражением, не является допустимым запросом, вы группируете по user_id и выбор множества других полей на основе этого, но не говорит БД, как он должен агрегировать другие файлы. Например, если ваши данные выглядят следующим образом:

a  | b
---|---
1  | 1
1  | 2
2  | 3

теперь, когда вы просите db группировать по a а также возвращает b, он не знает, как агрегировать значения 1,2. Вам нужно сказать, нужно ли выбрать min, max, average, sum или что-то еще. Как раз когда я писал есть два ответа, которые могли бы объяснить все это лучше.

в вашем случае использования, я думаю, вы не хотите, чтобы группа на уровне БД. Поскольку есть только 10 искусств, вы можете сгруппировать их в вашем приложении. Не используйте этот метод с тысячами искусств, хотя:

 arts = Art.all(:order => "created_at desc", :limit => 10)
 grouped_arts = arts.group_by {|art| art.user_id}
 # now you have a hash with following structure in grouped_arts
 # { 
 #    user_id1 => [art1, art4],
 #    user_id2 => [art3],
 #    user_id3 => [art5],
 #    ....
 # }

EDIT: выберите latest_arts, но только одно искусство на пользователя

просто чтобы дать вам представление о sql(не протестировали его, поскольку у меня нет СУБД, установленных на моем система)

SELECT arts.* FROM arts
WHERE (arts.user_id, arts.created_at) IN 
  (SELECT user_id, MAX(created_at) FROM arts
     GROUP BY user_id
     ORDER BY MAX(created_at) DESC
     LIMIT 10)
ORDER BY created_at DESC
LIMIT 10

это решение основано на практическом предположении, что никакие два искусства для одного и того же пользователя не могут иметь одинаковый самый высокий created_at, но это может быть неправильно, если вы импортируете или программно создаете большую часть искусств. Если предположение не выполняется, sql может получить более contrieved.

EDIT: попытка изменить запрос на Arel:

Art.where("(arts.user_id, arts.created_at) IN 
             (SELECT user_id, MAX(created_at) FROM arts
                GROUP BY user_id
                ORDER BY MAX(created_at) DESC
                LIMIT 10)").
    order("created_at DESC").
    page(params[:page]).
    per(params[:per])

вам нужно выбрать конкретные столбцы, которые вам нужны

Art.выберите(:ид_пользователя).группа(:ид_пользователя).ограничение(10)

это вызовет ошибку при попытке выбрать заголовок в запросе, например

Art.выберите (: user_id,: title).группа(:ид_пользователя).ограничение(10)

столбец "искусства.заголовок " должен отображаться в предложении GROUP BY или использоваться в агрегатной функции

Это потому, что при попытке группы user_id, запрос понятия не имеет, как обрабатывать заголовок в группе, потому что группа содержит несколько заголовков.

таким образом, исключение уже упоминалось, что вам нужно появиться в group by

Art.выберите (: user_id,: title).group (: user_id,: title).ограничение(10)

или использоваться в агрегатной функции

Art.выберите ("user_id, array_agg (title) В качестве заголовков").группа(:ид_пользователя).ограничение(10)


взгляните на этот пост SQLite в группу Postgres (Heroku) по

PostGres фактически следует стандарту SQL здесь, в то время как sqlite и mysql отрываются от стандарта.


взгляните на этот вопрос -преобразование MySQL select в PostgreSQL. Postgres не позволит столбцу быть перечисленным в инструкции select, который не входит в предложение group by.