Skip to main content

ActiveRecord group and count — SQL GROUP BY in Rails

How to use group and count in ActiveRecord for SQL GROUP BY. With having, order, and the SQL Rails generates. Rails 7/8 production examples with output.

A
Raza Hussain
· 3 min read · 2
ActiveRecord group and count — SQL GROUP BY in Rails

SQL GROUP BY is one of the most-used aggregation patterns. ActiveRecord supports it directly, but the return type surprises developers coming from SQL.

Basic group and count

SQL:

SELECT status, COUNT(*) FROM orders GROUP BY status

ActiveRecord:

Order.group(:status).count
# => {"pending"=>12, "shipped"=>34, "cancelled"=>5}

The return type is a Hash, not a Relation. You cannot chain further after .count.

Grouping by Multiple Columns

SQL:

SELECT status, country, COUNT(*) FROM orders GROUP BY status, country

ActiveRecord:

Order.group(:status, :country).count
# => {["pending", "US"]=>8, ["pending", "UK"]=>4, ...}

Other Aggregates

Order.group(:status).sum(:total_cents)
Order.group(:status).average(:total_cents)
Order.group(:status).maximum(:total_cents)
Order.group(:status).minimum(:total_cents)

All return a Hash with the group value as key.

Adding HAVING

SQL:

SELECT status, COUNT(*) FROM orders GROUP BY status HAVING COUNT(*) > 10

ActiveRecord:

Order.group(:status).having("COUNT(*) > ?", 10).count

.having takes a string condition with the same sanitization rules as .where. Always use ? placeholders.

Ordering Grouped Results

Order.group(:status).order("count_all DESC").count
# The alias 'count_all' is what Rails uses for COUNT(*)

If you’re ordering by a specific aggregate column:

Order.group(:status).order("sum_total_cents DESC").sum(:total_cents)

With select and group Together

If you need both the grouped data and other columns:

Order.select("status, COUNT(*) as order_count, SUM(total_cents) as revenue")
     .group(:status)
# Returns a Relation with virtual attributes order_count and revenue

Access them:

results = Order.select("status, COUNT(*) as order_count").group(:status)
results.each { |r| puts "#{r.status}: #{r.order_count}" }

Grouping by Date

SQL:

SELECT DATE(created_at), COUNT(*) FROM orders GROUP BY DATE(created_at)

ActiveRecord:

Order.group("DATE(created_at)").count
# => {"2026-09-01"=>45, "2026-09-02"=>62, ...}

For PostgreSQL, DATE_TRUNC is more precise:

Order.group("DATE_TRUNC('month', created_at)").count

The N+1 You Don’t Expect

Avoid this pattern:

Status.all.each do |status|
  count = Order.where(status: status.name).count
  # Separate query per status — this is N+1
end

Replace with:

Order.group(:status).count
# One query. Use the hash.

What Rails Generates

Order.group(:status).count
# SELECT status, COUNT(*) AS count_all FROM orders GROUP BY status

Always verify with .to_sql before adding .count (which forces execution):

Order.group(:status).to_sql

Quick Reference

Goal ActiveRecord
Count per group .group(:col).count
Sum per group .group(:col).sum(:amount)
Filter groups .group(:col).having(“COUNT(*) > ?”, n)
Group by date .group(“DATE(created_at)”).count
Group + custom select .select(“col, COUNT(*) as n”).group(:col)

Was this article helpful?

Your feedback helps us improve our content

Be the first to vote!

How We Verify Conversions

Every conversion shown on this site follows a strict verification process to ensure correctness:

  • Compare results on same dataset — We run both SQL and ActiveRecord against identical test data and verify results match
  • Check generated SQL with to_sql — We inspect the actual SQL Rails generates to catch semantic differences (INNER vs LEFT JOIN, WHERE vs ON, etc.)
  • Add regression tests for tricky cases — Edge cases like NOT EXISTS, anti-joins, and predicate placement are tested with multiple scenarios
  • Tested on Rails 8.1.1 — All conversions verified on current Rails version to ensure compatibility

Last updated: September 23, 2026

Try These Queries in Our Converter

See the SQL examples from this article converted to ActiveRecord—and compare the SQL Rails actually generates.

2

Leave a Response

Responses (0)

No responses yet

Be the first to share your thoughts

R

Raza Hussain

Full-stack developer specializing in Ruby on Rails, React, and modern JavaScript. 15+ years upgrading and maintaining production Rails apps. Led Rails 4/5 → 7 upgrades with 40% performance gains, migrated apps from Heroku to Render cutting costs by 35%, and built systems for StatusGator, CryptoZombies, and others. Available for Rails upgrades, performance work, and cloud migrations.

💼 15 years experience 📝 49 posts