事实证明,实际上有一种方法可以在 SQL 中执行此操作。首先,让我们设置一些测试环境:
rails new foobar
cd foobar
rails g model Product name:string points:integer
rake db:migrate
rails console
在 Rails 控制台中,向数据库提供一些记录:
Product.new(name: 'Foo', points: 1).save!
Product.new(name: 'Bar', points: 2).save!
Product.new(name: 'Baz', points: 3).save!
Product.new(name: 'Baf', points: 4).save!
Product.new(name: 'Quux', points: 5).save!
Now i found a way of getting running totals in SQL in this post here. It works like this:
query = <<-SQL
SELECT *, (
SELECT SUM(points)
FROM products
WHERE id <= p.id
) AS total_points
FROM products p
SQL
Running this query against the test DB gives us:
Product.find_by_sql(query).each do |p|
puts p.name.ljust(5) + p.points.to_s.rjust(2) + p.total_points.to_s.rjust(3)
end
# Foo 1 1
# Bar 2 3
# Baz 3 6
# Baf 4 10
# Quux 5 15
So we can now use a HAVING
clause (and a GROUP BY
because this is needed for HAVING
)to fetch only the products that match the condition and LIMIT
the number of results to one:
query = <<-SQL
SELECT *, (
SELECT SUM(points)
FROM products
WHERE id <= p.id
) AS total_points
FROM products p
GROUP BY p.id
HAVING total_points >= #{find_point_level}
LIMIT 1
SQL
I'm really curious how this performs in your environment with many many records. Give it a try and tell me if it works for you, if you like.