Reputation: 19525
I have the following code (note the includes
and the .each
):
subscribers = []
mailgroup.mailgroup_members.opted_to_receive_email.includes(:roster_contact, :roster_info).each { |m|
subscribers << { :EmailAddress => m.roster_contact.member_email,
:Name => m.roster_contact.member_name,
:CustomFields => [ { :Key => 'gender',
:Value => m.roster_info.gender.present? ? m.roster_info.gender : 'X'
} ]
} if m.roster_contact.member_email.present?
}
subscribers
Correspondingly, I see the following in my logs (i.e. select * from ROSTER_INFO ... IN (...)
):
SELECT `ROSTER_INFO`.* FROM `ROSTER_INFO` WHERE `ROSTER_INFO`.`ID` IN ('1450', '1000', '1111')
Yet immediately after that there are select * from ROSTER_INFO
for each ID already specified in the IN
list of the previous query:
RosterInfo Load (84.8ms) SELECT `ROSTER_INFO`.* FROM `ROSTER_INFO` WHERE `ROSTER_INFO`.`ID` = '1450' LIMIT 1
RosterInfo Load (59.2ms) SELECT `ROSTER_INFO`.* FROM `ROSTER_INFO` WHERE `ROSTER_INFO`.`ID` = '1000' LIMIT 1
RosterInfo Load (56.8ms) SELECT `ROSTER_INFO`.* FROM `ROSTER_INFO` WHERE `ROSTER_INFO`.`ID` = '1111' LIMIT 1
If select *
had already been done on ROSTER_INFO
on all IDs of interest (IN (...)
), why is another select *
being done again for each of the same IDs? Doesn't ActiveRecord
already know all the ROSTER_INFO
columns for each ID?
(Meanwhile, there are no individual queries for ROSTER_CONTACT
, yet if I remove :roster_contact
from the includes
method, then ROSTER_INFO
is not queried again, but ROSTER_CONTACT
is.)
RosterInfo model (abridged)
class RosterInfo < ActiveRecord::Base
self.primary_key = 'ID'
end
RosterContact model (abridged)
class RosterContact < ActiveRecord::Base
self.primary_key = 'ID'
has_many :mailgroup_members, foreign_key: 'rosterID'
has_many :mailgroups, through: :mailgroup_members
has_one :roster_info, foreign_key: 'ID' # can use this line
#belongs_to :roster_info, foreign_key: 'ID' # or this with no difference
def member_name # I added this method to this
roster_info.member_name # question only *after* having
end # figured out the problem.
end
RosterWeb model (abridged)
class RosterWeb < ActiveRecord::Base
self.primary_key = 'ID'
end
Mailgroup model (abridged)
class Mailgroup < ActiveRecord::Base
self.primary_key = 'ID'
has_many :mailgroup_members, foreign_key: 'mailCatID'
has_one :mailing_list, foreign_key: :legacy_id
end
MailgroupMember model (abridged)
class MailgroupMember < ActiveRecord::Base
self.primary_key = 'ID'
belongs_to :mailgroup, foreign_key: 'mailCatID'
belongs_to :roster_contact, foreign_key: 'rosterID'
belongs_to :roster_info, foreign_key: 'rosterID'
belongs_to :roster_web, foreign_key: 'rosterID'
scope :opted_to_receive_email, joins(:roster_web).where('ROSTER_WEB.receiveEmail=?', 1)
end
Upvotes: 8
Views: 266
Reputation: 19525
The issue turned out to be related to m.roster_contact.member_name
-- unfortunately I made member_name
a method of roster_contact
that itself (indirectly) queried roster_info.member_name
. I resolved this by changing the line
:Name => m.roster_contact.member_name,
to directly query roster_info
as follows
:Name => m.roster_info.member_name,
I am sorry for the trouble!
Upvotes: 1
Reputation: 7101
class RosterInfo < ActiveRecord::Base
has_one :roster_contact, foreign_key: 'ID'
end
class RosterContact < ActiveRecord::Base
has_one :roster_info, foreign_key: 'ID'
end
I don't know what is the premise for having bi-directional has_one
, but I suspect it will turn out badly. Probably change one of them to belongs_to
. Do the same for the other bi-directional has_one
associations.
Another thing is that you are using 'ID' for the foreign_key column, where the usual practice is roster_contact_id
or whichever class you are referencing.
Edit:
On closer examination, RosterInfo
, RosterContact
, RosterWeb
look like separate tables for what should be a single record since they are all having the same set of mutual has_one
associations. This is something that should be addressed on the schema level, but right now you should be able to drop the has_one
associations from one of the three models to solve your immediate problem.
Upvotes: 0
Reputation: 128
I'm going to stick my neck out and say that this is probably an in-flight optimization by your query engine. The 'IN' is typically used to compare large sets of keys, the most efficient way of resolving three keys (assuming ID is the key) would be to retrieve each row by key, as has happened.
Upvotes: 0