Rails has_many считает количество дочерних строк - PullRequest
25 голосов
/ 28 апреля 2009

Каков "путь рельсов" для эффективного захвата всех строк родительской таблицы вместе с количеством детей в каждой строке?

Я не хочу использовать counter_cache, так как я хочу запустить эти подсчеты на основе некоторых временных условий.

Пример блога клише: Таблица статей. Каждая статья имеет 0 или более комментариев.

Я хочу получить количество комментариев к каждой статье за ​​прошедший час, день, неделю.

Однако в идеале я не хочу перебирать список и делать отдельные вызовы sql для каждой статьи, а также не хочу использовать :include для предварительной выборки всех данных и их обработки на сервере приложений.

Я хочу выполнить один оператор SQL и получить один набор результатов со всей информацией.

Я знаю, что могу жестко закодировать полный SQL, и, возможно, мог бы использовать .find и просто установить параметры :joins, :group и :conditions ... НО мне интересно, есть ли "лучший" путь ... он же "путь рельсов"

Заранее спасибо

Ответы [ 5 ]

28 голосов
/ 28 апреля 2009

Этот вызов 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 |
+----+----------------+------------+
9 голосов
/ 04 апреля 2013

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.

5 голосов
/ 22 ноября 2014

Простой способ, который я использовал для решения этой проблемы, был

В моей модели я сделал:

class Article < ActiveRecord::Base
  has_many :posts

  def count_posts
    Post.where(:article_id => self.id).count
  end
end

Теперь вы можете использовать, например:

Articles.first.count_posts

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

4 голосов
/ 28 апреля 2009

С точки зрения SQL это выглядит тривиально - просто напишите новый запрос.

С точки зрения Rails, упомянутые вами значения являются вычисленными значениями. Поэтому, если вы используете find_by_sql, класс Model не будет знать о вычисляемых полях и, следовательно, будет возвращать вычисленные значения в виде строк, даже если вам удастся перевести запрос в язык Rails. См. Связанный вопрос ниже.
Общее отклонение (от ответов, которые я получил на этот вопрос) заключалось в том, чтобы отдельный класс отвечал за сведение / вычисление желаемых значений.

Как заставить рельсы возвращать атрибуты SUM (columnName) с правильным типом данных вместо строки?

0 голосов
/ 23 апреля 2019

Я сделал эту работу следующим образом:

def show
  section = Section.find(params[:id])
  students = Student.where( :section_id => section.id ).count
  render json: {status: 'SUCCESS', section: students},status: :ok
end

В этом у меня было 2 модели Section и Student. Поэтому я должен считать количество студентов, которые соответствуют определенному идентификатору раздела.

...