Rails имеет дочерние строки count
Что такое "путь рельсов", чтобы эффективно захватывать все строки родительской таблицы вместе с количеством числа детей, которое имеет каждая строка?
Я не хочу использовать counter_cache
, поскольку я хочу запустить эти подсчеты на основе некоторых временных условий.
Пример блога клише:
Таблица статей. В каждой статье есть 0 или более комментариев.
Я хочу уметь вытягивать количество комментариев каждой статьи за прошлый час, день, неделю.
Однако в идеале я не хочу перебирать список и создавать отдельные вызовы sql для каждой статьи, а также не хочу использовать :include
для предварительной выборки всех данных и обработки их на сервере приложений.
Я хочу запустить один оператор SQL и получить один результирующий набор со всей информацией.
Я знаю, что могу полностью закодировать полный SQL и, возможно, использовать .find
и просто установить параметры :joins
, :group
и :conditions
... НО Мне интересно, есть ли "лучший" способ... иначе "путь рельсов"
Заранее спасибо
Ответы
Ответ 1
Этот вызов activerecord должен делать то, что вы хотите:
Article.find(:all, :select => 'articles.*, count(posts.id) as post_count',
:joins => 'left outer join posts on posts.article_id = articles.id',
:group => 'articles.id'
)
Это вернет список объектов статьи, каждый из которых имеет на нем метод post_count
, который содержит количество сообщений в статье в виде строки.
Метод выполняет sql, подобный следующему:
SELECT articles.*, count(posts.id) AS post_count
FROM `articles`
LEFT OUTER JOIN posts ON posts.article_id = articles.id
GROUP BY articles.id
Если вам интересно, это пример результатов MySQL, которые вы можете увидеть при запуске такого запроса:
+----+----------------+------------+
| id | text | post_count |
+----+----------------+------------+
| 1 | TEXT TEXT TEXT | 1 |
| 2 | TEXT TEXT TEXT | 3 |
| 3 | TEXT TEXT TEXT | 0 |
+----+----------------+------------+
Ответ 2
Версия Rails 3
Для Rails 3 вы будете выглядеть примерно так:
Article.select( "articles.*, count(comments.id) AS comments_count" )
.joins( "LEFT OUTER JOIN comments ON comments.article_id = articles.id" )
.group( "articles.id" )
Благодаря Gdeglin для версии Rails 2.
Ответ 3
Простым способом, который я использовал для решения этой проблемы, был
В моей модели я сделал:
class Article < ActiveRecord::Base
has_many :posts
def count_posts
Post.where(:article_id => self.id).count
end
end
Теперь вы можете использовать, например:
Articles.first.count_posts
Я не уверен, что это может быть более эффективным способом, Но его решение и, на мой взгляд, более элегантно, чем другие.
Ответ 4
С точки зрения SQL это выглядит тривиально. Просто напишите новый запрос.
С точки зрения Rails значения, которые вы указываете, являются вычисленными значениями. Поэтому, если вы используете find_by_sql
, класс Model не будет знать о вычисленных полях и, следовательно, вернет вычисленные значения в виде строк, даже если вам удастся перевести запрос в Rails. См. Связанный вопрос ниже.
Общий дрейф (из ответов, которые я получил на этот вопрос) заключался в том, чтобы отдельный класс отвечал за свертывание/вычисление желаемых значений.
Как получить рельсы для возврата атрибутов SUM (columnName) с правильным типом данных вместо строки?