SeattleDucati
SeattleDucati

Reputation: 293

Rails 4 - Am I causing excessive Active Record queries?

I'm a newer dev, and I want to know if I'm causing excessive queries with an index method.

The index method for my Ownerships model looks like this:

 def index
  @ownerships = Ownership.all.select { |t| t.player_id == nil}

  Rails.application.config.counter = (Ownership.all.count - Ownership.all.select{ |t| t.player_id == nil}.count) + 1
  end

It works fine. The goal is to set the counter equal to the id of the first Ownership that does not have a player assigned. But I'm concerned that I'm causing 3 separate queries every time the index action is called. Is that the case?

I know I can find the first record with 'nil' Player like this:

 Ownership.all.select { |t| t.player_id == nil}.first(1)

Is there a simple way to set the counter equal to the ID of the record that is found?

Thank you.

Upvotes: 0

Views: 63

Answers (2)

Andrey Deineko
Andrey Deineko

Reputation: 52357

Using Ruby in AR is inefficient and I suggest using it as a last resort.

Always try to get to the database layer.

The following AR query

 Ownership.find_by(player_id: nil)

fires single SQL query:

SELECT  `ownerships`.* 
FROM `ownerships` 
WHERE `ownerships`.`player_id` = NULL 
LIMIT 1

and does exactly what you need.

Be aware, that with find_by the following would result in NoMethodError, if no record found

 Ownership.find_by(player_id: nil).id 
 #=> NoMethodError: undefined method `id' for nil:NilClass

Take a look into finders docs for more info on AR querying.

Upvotes: 2

Shkarik
Shkarik

Reputation: 1159

You are selecting all existing Ownerships and then process them in Ruby - which is very inefficient. What if you got 10k Ownerships? Or 100k? Not only the query to database is too heavy, but also Ruby can exhaust the memory if collection is too big.

What you might want to do here is using SQL to find all needed records. In Rails, you can get SQL generated for you by ActiveRecord queries, like this:

@ownerships = Ownership.where(player_id: nil)

or

@ownership = Ownership.find_by(player_id: nil)

to find the first matching one.

Upvotes: 2

Related Questions