Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The Archive Base

The Archive Base Logo The Archive Base Logo

The Archive Base Navigation

  • SEARCH
  • Home
  • About Us
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • Add group
  • Groups page
  • Feed
  • User Profile
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Buy Points
  • Users
  • Help
  • Buy Theme
  • SEARCH
Home/ Questions/Q 772113
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: May 14, 20262026-05-14T18:45:35+00:00 2026-05-14T18:45:35+00:00

I’m trying to avoid the N+1 queries problem with eager loading, but it’s not

  • 0

I’m trying to avoid the N+1 queries problem with eager loading, but it’s not working. The associated models are still being loaded individually.

Here are the relevant ActiveRecords and their relationships:

class Player < ActiveRecord::Base
  has_one :tableau
end

Class Tableau < ActiveRecord::Base
  belongs_to :player
  has_many :tableau_cards
  has_many :deck_cards, :through => :tableau_cards
end

Class TableauCard < ActiveRecord::Base
  belongs_to :tableau
  belongs_to :deck_card, :include => :card
end

class DeckCard < ActiveRecord::Base
  belongs_to :card
  has_many :tableaus, :through => :tableau_cards
end

class Card < ActiveRecord::Base
  has_many :deck_cards
end

class Turn < ActiveRecord::Base
  belongs_to :game
end

and the query I’m using is inside this method of Player:

def tableau_contains(card_id)
  self.tableau.tableau_cards = TableauCard.find :all, :include => [ {:deck_card => (:card)}], :conditions => ['tableau_cards.tableau_id = ?', self.tableau.id]
  contains = false
  for tableau_card in self.tableau.tableau_cards
    # my logic here, looking at attributes of the Card model, with        
    # tableau_card.deck_card.card;
    # individual loads of related Card models related to tableau_card are done here
  end
  return contains
end

Does it have to do with scope? This tableau_contains method is down a few method calls in a larger loop, where I originally tried doing the eager loading because there are several places where these same objects are looped through and examined. Then I eventually tried the code as it is above, with the load just before the loop, and I’m still seeing the individual SELECT queries for Card inside the tableau_cards loop in the log. I can see the eager-loading query with the IN clause just before the tableau_cards loop as well.

EDIT: additional info below with the larger, outer loop

EDIT2 : corrected loop below with tips from answers

EDIT3 : added more details in loop with goals

Here’s the larger loop. It is inside an observer on after_save

def after_save(pa)
  turn = Turn.find(pa.turn_id, :include => :player_actions)
  game = Game.find(turn.game_id, :include => :goals)
  game.players.all(:include => [ :player_goals, {:tableau => [:tableau_cards => [:deck_card => [:card]]]} ])
  if turn.phase_complete(pa, players)  # calls player.tableau_contains(card)
    for goal in game.goals
      if goal.checks_on_this_phase(pa)
        if goal.is_available(players, pa, turn)
          for player in game.players
            goal.check_if_player_takes(player, turn, pa)
              ... # loop through player.tableau_cards
            end
          end
        end
      end
    end
  end

Here’s the relevant code in the turn class:

def phase_complete(phase, players)
  all_players_complete = true
  for player in players
    if(!player_completed_phase(player, phase))
      all_players_complete = false
    end
  end
  return all_players_complete
end

the for player in game.players is doing another query to load the players. It is cached, I mean it has the CACHE label in the log, but I would’ve thought there would be no query at all because the game.players should already be loaded in memory.

Another snippet from the Goal model:

class Goal < ActiveRecord::Base
  has_many :game_goals
  has_many :games, :through => :game_goals
  has_many :player_goals
  has_many :players, :through => :player_goals

  def check_if_player_takes(player, turn, phase)
    ...
    for tab_card in player.tableau_cards
    ...
  end
end
  • 1 1 Answer
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

1 Answer

  • Voted
  • Oldest
  • Recent
  • Random
  1. Editorial Team
    Editorial Team
    2026-05-14T18:45:36+00:00Added an answer on May 14, 2026 at 6:45 pm

    Try this:

    class Game
      has_many :players
    end
    

    Change the logic of tableau_contains as follows:

    class Player < ActiveRecord::Base
      has_one :tableau
      belongs_to :game
    
      def tableau_contains(card_id)
        tableau.tableau_cards.any?{|tc| tc.deck_card.card.id == card_id}
      end
    
    end
    

    Change the logic of after_save as follows:

    def after_save(turn)
      game = Game.find(turn.game_id, :include => :goals))
      Rails.logger.info("Begin  eager loading..")                
      players = game.players.all(:include => [:player_goals,
                {:tableau => [:tableau_cards=> [:deck_card => [:card]]]} ])
      Rails.logger.info("End  eager loading..")                
      Rails.logger.info("Begin  tableau_contains check..")                
      if players.any?{|player| player.tableau_contains(turn.card_id)}
        # do something..                
      end
      Rails.logger.info("End  tableau_contains check..")                
    end
    

    Second line in the after_save method eager loads the data needed to perform the tableau_contains check. The calls such as tableau.tableau_cards and tc.deck_card.card should/will not hit the DB.

    Issues in your code:

    1) Assigning array to an has_many association

    @game.players = Player.find :all, :include => ...
    

    Statement above is not a simple assignment statement. It changes the palyers table rows with the game_id of the given game.
    I am assuming that is not what you want. If you check the DB table you will notice that the updated_time of the players table
    rows have changed after assignment.

    You have to assign the value to a separate variable as shown in the code sample in after_save method.

    2) Hand coded association SQL

    Many places in the code you are hand coding the SQL for association data. Rails provides associations for this.

    E.g:

    tcards= TableauCard.find :all, :include => [ {:deck_card => (:card)}], 
             :conditions => ['tableau_cards.tableau_id = ?', self.tableau.id]
    

    Can be rewritten as:

    tcards = tableau.tableau_cards.all(:include => [ {:deck_card => (:card)}])
    

    The tableau_cards cards association on Tableau model constructs the same SQL you have hand coded.

    You can further improve the statement above by adding a has_many :through association to Player class.

    class Player
      has_one :tableau
      has_many :tableau_cards, :through => :tableau
    end
    
    tcards = tableau_cards.all(:include => [ {:deck_card => (:card)}])
    

    Edit 1

    I created an application to test this code. It works as expected. Rails runs several SQL to eager load the data, i.e.:

    Begin  eager loading..
    SELECT * FROM `players` WHERE (`players`.game_id = 1) 
    SELECT `tableau`.* FROM `tableau` WHERE (`tableau`.player_id IN (1,2))
    SELECT `tableau_cards`.* FROM `tableau_cards` 
              WHERE (`tableau_cards`.tableau_id IN (1,2))
    SELECT * FROM `deck_cards` WHERE (`deck_cards`.`id` IN (6,7,8,1,2,3,4,5))
    SELECT * FROM `cards` WHERE (`cards`.`id` IN (6,7,8,1,2,3,4,5))
    End  eager loading..
    Begin  tableau_contains check..
    End  tableau_contains check..
    

    I don’t see any SQL executed after eager loading the data.

    Edit 2

    Make the following change to your code.

    def after_save(pa)
      turn = Turn.find(pa.turn_id, :include => :player_actions)
      game = Game.find(turn.game_id, :include => :goals)
      players = game.players.all(:include => [ :player_goals, {:tableau => [:tableau_cards => [:deck_card => [:card]]]} ])
      if turn.phase_complete(pa, game, players)
        for player in game.players
          if(player.tableau_contains(card))
          ...
          end
        end
      end
    end
    def phase_complete(phase, game, players)
      all_players_complete = true
      for player in players
        if(!player_completed_phase(player, phase))
          all_players_complete = false
        end
      end
      return all_players_complete
    end
    

    The caching works as follows:

    game.players # cached in the game object
    game.players.all # not cached in the game object
    
    players = game.players.all(:include => [:player_goals])
    players.first.player_goals # cached
    

    The second statement above results in a custom association query. Hence AR doesn’t cache the results. Where as player_goals are cached for every player object in the 3rd statement as they are fetched using standard association SQL.

    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Ask A Question

Stats

  • Questions 404k
  • Answers 404k
  • Best Answers 0
  • User 1
  • Popular
  • Answers
  • Editorial Team

    How to approach applying for a job at a company ...

    • 7 Answers
  • Editorial Team

    What is a programmer’s life like?

    • 5 Answers
  • Editorial Team

    How to handle personal stress caused by utterly incompetent and ...

    • 5 Answers
  • Editorial Team
    Editorial Team added an answer $mysql_query($query); => mysql_query($query);. Note the missing dollar. If you try… May 15, 2026 at 5:27 am
  • Editorial Team
    Editorial Team added an answer You do not place environment specific stuff in the persistence.xml.… May 15, 2026 at 5:27 am
  • Editorial Team
    Editorial Team added an answer I believe the problem is that you should use $output… May 15, 2026 at 5:27 am

Trending Tags

analytics british company computer developers django employee employer english facebook french google interview javascript language life php programmer programs salary

Top Members

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help
  • SEARCH

Footer

© 2021 The Archive Base. All Rights Reserved
With Love by The Archive Base

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.