Использование GROUP BY и COUNT в Rails ActiveRecord
Быстрый ответ
Model.group(:attribute).count
Создание частотного списка полей осуществляется через .group(:attribute).count
. Например, подсчет числа пользователей в соответствии с ролями составляется таким образом:
User.group(:role).count # => { 'admin' => 3, 'user' => 13 }
В этом случае ключ и значение представляют собой роль и соответствующее ей количество пользователей.

SQL для людей, или тайны ActiveRecord
ActiveRecord переводит запросы Ruby в чистый SQL. Исходное преобразование выглядит следующим образом:
Person.group(:name).count
Запрос в SQL получается таким:
SELECT COUNT(*) AS count_all, name AS name FROM "people" GROUP BY "people"."name"
Столбец name
группируется, а количество уникальных имён записывается в таблицу, где результат представляет собой хеш-карту имен и их количества.
Перепись населения с джоинами, или как освоить сложные запросы
При работе со множеством таблиц используйте функциональность ActiveRecord
:
Order.joins(:customer).group('customers.name').size
Этот запрос выдаст количество заказов, сгруппированных по именам клиентов, с учетом их объединения в таблицы. Метод size
по своему действию схож с count
, но предпочтителен при использовании джойнов.
Больше данных для больших знаний: .select() и .group()
Временами, для получения дополнительной информации, необходимо использовать следующее:
Person.select(:name, 'COUNT(name) as name_count').group(:name)
При помощи select
можно выбрать конкретные столбцы. В результате к имени добавляется его количество ('name_count'), и результат остается в виде ActiveRecord::Relation
, который содержит имена и их количество.
Один запрос — все ответы: оптимизация запросов
Для повышения эффективности необходимо оптимизировать запросы:
Person.select(:name, 'COUNT(name) as name_count').joins(:articles).group(:name)
Такой способ позволяет получить список людей с числом их статей за один SQL-запрос, что упрощает проведение последующих операций.
Функция distinct()
Для определения уникальности атрибута используйте distinct
:
Person.select(:name).distinct.count # => Integer
Таким образом, вы получите количество уникальных имен без повторений. Запомните, что distinct
и group
– это разные инструменты!
Визуализация
Представьте наглядно разноцветные шарики. После группировки по цвету они разделяются на отдельные кучи:
🟠🔵🟢🔴🔵🟣🟠🔵🟢
Их количество в каждой куче:
markdown 🟠 Количество: 2 🔵 Количество: 3 🟢 Количество: 2 🔴 Количество: 1 🟣 Количество: 1
Именно так происходит группировка и подсчет в ActiveRecord.
### Станьте профессионалом с помощью советов от профи:
1. **Сфокусированность**: Используйте "scopes" для стандартизации запросов внутри модели.
2. **NULL значения**: NULL значения группируются вместе; обрабатывайте их согласно логике вашего приложения.
3. **Клауза Having**: Используйте `having` для фильтрации результатов после группировки. `where` фильтрует до группировки, `having` — после.
### Ловушки и подводные камни:
- **Перегрузка запросов**: Не включайте ненужные поля, так как это замедлит выполнение запросов.
- **Проблема N+1**: Избегайте загрузки данных для каждого экземпляра модели, используйте `includes`.
- **Недопонимание `count`**: `count` считает строки, `group` основывается на группах, `distinct` считает уникальные значения.
## Полезные материалы
1. [Active Record Query Interface — Ruby on Rails Guides](https://guides.rubyonrails.org/active_record_querying.html#group) — Руководство по использованию `GROUP BY` в ActiveRecord.
2. [group (ActiveRecord::QueryMethods) – APIdock](https://apidock.com/rails/ActiveRecord/QueryMethods/group) — Документация по методу `group`.
3. [Understanding 'GROUP BY' in Rails – Tutorial | DigitalOcean](https://www.digitalocean.com/community/tutorials/understanding-group-by-in-rails-for-beginners) — Простое объяснение `GROUP BY` для новичков.
4. [Module: Enumerable (Ruby 2.7.0)](https://ruby-doc.org/core-2.7.0/Enumerable.html#method-i-group_by) — Документация по Enumerable `group_by` в Ruby.
5. [Back to Basics: Writing SQL Queries](https://thoughtbot.com/blog/back-to-basics-sql) — Гид по SQL, полезный для работы с ActiveRecord!