简体   繁体   English

活动记录中的多个查询

[英]Multiple queries in active record

If I run this query in rails console,如果我在 Rails 控制台中运行此查询,

def c
  ids = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  users = User.all
  members = Member.where(id: ids).includes(:locations)
  user_id_to_member_id_mapping = {} # Not empty. hash that gives member_id for each userid  
  users.each do |user|
    puts members.find_by(id: user_id_to_member_id_mapping[user.id]).location
  end
end

multiple queries (Member object queries) are fired (one for each value of i ).多个查询(成员对象查询)被触发(每个i值一个)。 Are the members already in memory ( members variable)?成员是否已经在内存中( members变量)? How can I get the data needed above to make a single query?如何获取上述所需的数据以进行单个查询?

Member Load (0.2ms)  SELECT  `members`.* FROM `members` WHERE `members`.`id` IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10) AND `members`.`id` = 1 LIMIT 1
  ProfileAnswer Load (0.4ms)  SELECT `profile_answers`.* FROM `profile_answers` WHERE `profile_answers`.`member_id` = 1
  Member Load (0.4ms)  SELECT  `members`.* FROM `members` WHERE `members`.`id` IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10) AND `members`.`id` = 2 LIMIT 1
  ProfileAnswer Load (0.2ms)  SELECT `profile_answers`.* FROM `profile_answers` WHERE `profile_answers`.`member_id` = 2
  Member Load (0.2ms)  SELECT  `members`.* FROM `members` WHERE `members`.`id` IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10) AND `members`.`id` = 3 LIMIT 1
  ProfileAnswer Load (0.2ms)  SELECT `profile_answers`.* FROM `profile_answers` WHERE `profile_answers`.`member_id` = 3
  Member Load (0.2ms)  SELECT  `members`.* FROM `members` WHERE `members`.`id` IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10) AND `members`.`id` = 4 LIMIT 1
  ProfileAnswer Load (0.2ms)  SELECT `profile_answers`.* FROM `profile_answers` WHERE `profile_answers`.`member_id` = 4
  Member Load (0.2ms)  SELECT  `members`.* FROM `members` WHERE `members`.`id` IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10) AND `members`.`id` = 5 LIMIT 1
  ProfileAnswer Load (0.2ms)  SELECT `profile_answers`.* FROM `profile_answers` WHERE `profile_answers`.`member_id` = 5
  Member Load (0.4ms)  SELECT  `members`.* FROM `members` WHERE `members`.`id` IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10) AND `members`.`id` = 6 LIMIT 1

I can't use member.each as the iterator is on a different condition.我不能使用member.each因为迭代器处于不同的条件。

Your code would be firing a query for each Member load because of the line由于该行,您的代码将针对每个Member负载触发查询

members.find_by(id: ids[i]).location

Here you have explicitly used find_by which is firing a query for each member also for location.在这里,您明确使用了find_by ,它同时为每个成员触发查询位置。

If reducing the query is priority then you may make use of ruby here如果减少查询是优先事项,那么您可以在此处使用 ruby

def c
  ids = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  users = User.all
  members = Member.where(id: ids).includes(:locations)
  user_id_to_member_id_mapping = {} # Not empty. hash that gives member_id for each userid  
  users.each do |user|
   puts members.find { |m| m.id == user_id_to_member_id_mapping[user.id] }&.location
end

Please note this would impact your performance if the Members count is very high.请注意,如果成员数量非常多,这会影响您的表现。

By your code, it is supposed that you have model Location with table name 'profile_answers' table.根据您的代码,假设您具有表名为“profile_answers”表的模型Location

Member has many profile_answers & has many locations through profile_answers .会员有许多 profile_answers 并且通过 profile_answers 拥有许多位置。

Then,然后,

members = Members.includes(profile_answers: :locations).where(id: ids)

members is a scope. members是一个范围。 members.find_by executes a query on scope. members.find_by在作用域上执行查询。 Hence an N+1 .因此是N+1

You want instead:你想要:

members =
  Member.
    where(id: ids).
    includes(:locations).
    to_a # greedy load

puts members.map(&:location) # or whatever

If the location is a model, you should use includes;如果location是模型,则应使用包含; includes will run a query to get your initial model 's data and the included models includes将运行查询以获取您的初始model的数据和包含的模型

program = Program.first
ids = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
members = Members.includes(:locations).where(id: ids)

Now when you get the first members, calling its location will be read from the memory.现在当你得到第一个成员时,调用它的位置将从内存中读取。

If the location is a column and you want to load all members, you can do:如果location是一列并且您想加载所有成员,则可以执行以下操作:

program = Program.first
ids = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
members = Members.where(id: ids)
locations = members.pluck(:location)

And finally, if you don't want to use the members, you can do:最后,如果你不想使用成员,你可以这样做:

program = Program.first
ids = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
locations = Members.where(id: ids).pluck(:location)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM