Подтвердить что ты не робот

Rails 3:.группа() упорядочена по created_at

Что такое способ Rails 3 для упорядочивания .group() приводит к Activerecord (здесь "created_at" )?

@messages = Message.group(:foo)

выводит только самое старое сообщение. Мне нужно, чтобы последние были показаны.

Я пробовал

@messages = Message.group(:foo).having("created_at = MAX(created_at)")

без успеха. Любые подсказки оценены!

Чтобы уточнить: я ищу, чтобы группа заказала внутри себя, а не обычный message.order( "..." ). Если не будет простого синтаксиса Activerecord, я был бы рад также сырым SQL


Обновление: попытка SQL-пути должна была работать:

@messages = Message.find_by_sql("
  SELECT messages.* 
  FROM messages 
  GROUP BY messages.foo 
  HAVING messages.created_at = MAX(messages.created_at) 
  ORDER BY messages.created_at DESC")

Но это извлекает только отдельные записи (те, которые не сгруппированы). Предположительно сгруппированные опускаются. Не знаю, почему все записи имеют: created_at и: foo values ​​

4b9b3361

Ответ 1

Все работает, используя более выделенный подзапрос, без вызова GROUP BY:

SELECT *
   FROM `messages`
   WHERE `id` = (
      SELECT `id`
      FROM `messages` as `alt`
      WHERE `alt`.`foo` = `messages`.`foo`
      ORDER BY `created_at` DESC
      LIMIT 1
   )
   ORDER BY `created_at` DESC

Все благодаря ответу Chaos в этой теме: Выберите 3 последних записи, где значения одного столбца отличаются

Пока не знаю о каких-либо скоростных последствиях. Если кто-то хочет вмешаться, пожалуйста, не стесняйтесь.

Ответ 2

Вы не можете использовать порядок, а затем группу в том же запросе в некоторых базах данных, поэтому вам нужно сделать это, используя два запроса:

message_ids = Message.select("MAX(id) AS id").group(:foo).collect(&:id)
@messages = Message.order("created_at DESC").where(:id => message_ids)

Примечание. Предполагается, что у вас есть auto-incremented столбец id, который делает большинство таблиц Rails.

Ответ 3

Вы можете пойти SQL, но вы также можете в большинстве случаев оставаться в зоне activerecord с помощью

@messages = Message.select('DISTINCT ON (foo) *').order(:created_at).reverse_order

Синтаксис DISTINCT ON () - postgres. В MYSQL есть аналогичный, но немного отличающийся синтаксис.

Я думаю, что этот запрос окажется намного более эффективным, чем принятый ответ, потому что это просто один вид и сканирование вместо N сокетов.

Ответ 4

Я только что столкнулся с этой проблемой. Для меня работал следующий запрос Rails:

Message.where("id IN (SELECT MAX(id) FROM messages GROUP BY id) AND state = 'unread'")

Здесь мы используем подзапрос, чтобы получить самый большой (и, следовательно, самый последний) ID в каждой группе, а затем отфильтровать те, чтобы показывать только те, в которых состояние == 'непрочитано'.

Совет. Я сделал метод self.latest в классе модели Message, который состоял из:

def self.latest
  where("id IN (SELECT MAX(id) FROM messages GROUP BY id)
end

Это означало, что я могу использовать его в таких контроллерах, как это:

Message.latest.where(state: 'unread')

Ответ 5

Я не вижу, как это можно было бы сделать с помощью Arel легко, но если вы просто приступите к созданию своего собственного SQL, как вы делали в своем вопросе, затем внесите следующие поправки:

@messages = Message.find_by_sql("
  SELECT messages.* 
  FROM messages 
  GROUP BY messages.foo 
  HAVING messages.created_at = (SELECT max(created_at) FROM messages AS sub_messages WHERE sub_messages.foo = messages.foo)

Обратите внимание на подзапрос в предложении HAVING. Предложение HAVING рассчитало MAX во всех ваших сообщениях - в отличие от сообщений только внутри foo. Подзапрос должен решить эту проблему.

Я сгладил имя таблицы сообщений в sub_messages, но это не обязательно. Я просто сделал это, чтобы показать, как работают родительский и подзапрос.