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.