4

У меня есть модель пользователя и модель карты. У пользователя много карточек, поэтому у карты есть атрибут user_id.ActiveRecord возвращает самую новую запись для каждого пользователя (уникально)

Я хочу получить самую новую единственную Карту для каждого пользователя. Я был в состоянии сделать это:

Card.all.order(:user_id, :created_at) 
# => gives me all the Cards, sorted by user_id then by created_at 

Это заставляет меня половину пути туда, и я мог бы, конечно, перебирать эти строки и захватить первый для каждого пользователя. Но мне это очень плохо пахнет, так как я много делаю с помощью массивов в Ruby.

Я также могу это сделать:

Card.select('user_id, max(created_at)').group('user_id') 
# => gives me user_id and created_at 

... но я только получить обратно user_ids и created_at метки времени. Я не могу выбрать любые другие столбцы (включая идентификатор), поэтому то, что я получаю, бесполезно. Я также не понимаю, почему PG не позволит мне выбрать больше столбцов, чем указано выше, не помещая их в group_by или совокупную функцию.

Я бы предпочел найти способ получить то, что я хочу, используя только ActiveRecord. Я также хочу написать этот запрос в raw SQL, но я не могу сделать это с AR. BTW, я использую DB Postgres, который ограничивает некоторые из моих параметров.

Спасибо, ребята.

+0

Как насчет 'Card.all.order (: user_id,: created_at) .first' или' Card.all.order (: user_id) .order (: created_at) .first' – Bala

+0

@Bala Я хочу, чтобы самая новая карта за пользователь для всех пользователей, которые сделали Карту. Это дает мне самую последнюю карту для пользователя с самым низким идентификатором, у которого также есть как минимум одна карта. – gregb

+0

Какой из них дал вам самый низкий идентификатор? просто добавьте ': desc' в': created_at' – Bala

ответ

0

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

Card.all.order(:user_id, :created_at).to_a.uniq(&:user_id) 

Это берет AR: отношение результатов, бросает их в Ruby Array, а затем выполняет Array # uniq по результатам с помощью Proc. После некоторого краткого тестирования появится #uniq сохранит порядок, так что пока все будет в порядке, прежде чем использовать uniq, вы должны быть хорошими.

Функция чувствительна к времени, поэтому я собираюсь использовать это сейчас, но я буду смотреть на что-то в необработанном SQL после ответа и ссылки Gene.

+1

Не уверен, почему это было завышено вне производительности. У любого есть какие-то мысли? – gregb

+0

Возможно, производительность, и чтобы она плавала на дне в результатах? Я только что протестировал ответ выше вашего и это действительно работает. Полученная вами ошибка может быть связана с проблемой с используемыми типами данных. Я просто использовал код в другом ответе, и он отлично работает. –

3

Мы присоединяемся стол карты на себя, на

а) first.id != second.id
б) first.user_id = second.user_id
с) first.created_at < second.created_at

Card.joins("LEFT JOIN cards AS c ON cards.id != c.id AND c.user_id = cards.user_id AND cards.created_at < c.created_at").where('c.id IS NULL') 
+0

получил эту ошибку: 'PG :: DatatypeMismatch: ERROR: аргумент HAVING должен иметь тип boolean, а не тип timestamp без часового пояса LINE 1: ... T" cards ". * FROM" cards "GROUP BY user_id HAVING MAX (create ... ' – gregb

+0

На самом деле я сам его протестировал, и он работает без ошибок, я обновил решения альтернативным способом. – mohameddiaa27

+0

Не работает с Postgres. Первая команда дает мне« PG :: DatatypeMismatch: ОШИБКА: 'и вторая дает мне' PG :: GroupingError: ERROR: '. – gregb

Смежные вопросы