Sql 加快给定循环的ActiveRecord插入速度 设置

Sql 加快给定循环的ActiveRecord插入速度 设置,sql,ruby-on-rails,postgresql,activerecord,ruby-on-rails-5.2,Sql,Ruby On Rails,Postgresql,Activerecord,Ruby On Rails 5.2,Rails 5.2、Ruby 2.5.1和Postgresql 问题 我们讨论的是一个包含数百万个条目的大数据集,并希望优化下面的代码,这将在SearchResult中创建数以万计的条目。插入它需要10秒以上的时间。有没有办法优化下面的代码,使其更快 search = Search.new(query: "Smith") Donation.joins(:donor). where("donors.name like ?", "%#{search.query}%").ea

Rails 5.2、Ruby 2.5.1和Postgresql

问题 我们讨论的是一个包含数百万个条目的大数据集,并希望优化下面的代码,这将在
SearchResult
中创建数以万计的条目。插入它需要10秒以上的时间。有没有办法优化下面的代码,使其更快

search = Search.new(query: "Smith")    
Donation.joins(:donor).
         where("donors.name like ?", "%#{search.query}%").each do |donation|
  search.search_results.build(donation: donation)
end
Donation.joins(:recipient).
         where("recipients.name like ?", "%#{search.query}%").each do |donation|
  search.search_results.build(donation: donation)
end
search.save

我不太喜欢在Rails中使用原始SQL,但是如果有一种方法可以在纯SQL中更快地解决这个问题,那么这也可能是可能的。

正如@matthewd所指出的,构建相关记录并保存父项实际上是可行的 您提出的代码可能有问题。实际上,active record的构建方法不会像您希望的那样保留搜索结果,如您在此处所见:

正确的替代方法是:

search = Search.new(query: "Smith")    
Donation.joins(:donor).
         where("donors.name like ?", "%#{search.query}%").each do |donation|
  search.search_results.create(donation: donation)
end
Donation.joins(:recipient).
         where("recipients.name like ?", "%#{search.query}%").each do |donation|
  search.search_results.create(donation: donation)
end
search.safe
当然,正如您所指出的,它根本没有效率,有两种方法可以解决这个问题。用一个叫做或手工制作的酷宝石

手工 这不是推荐的方法,但我把它放在这里供您参考。 这是您可以使用的SQL查询:

query = <<-SQL
INSERT INTO search_results (search_id, donation_id)
SELECT :search_id, id
FROM donations
INNER JOIN donor AS donor.id = donation.donor_id
WHERE donors.name LIKE :query
SQL
请注意以下几点:

  • 我在开始时使用了create,这样搜索就会被持久化,搜索结果会正确地引用它
  • 我使用find_each而不是each,它逐批查找记录,并且在迭代大量记录时通常更有效,您可以指定一个批大小作为该方法的一个选项
  • 我用所有搜索结果的非持久对象构建了一个数组,请注意,如果有很多捐赠,这将使用内存
  • 结果上没有uniq过滤器,我不知道这是否是预期行为,但请注意,您可能正在保存重复的结果

希望这将是有用的

你考虑过类似的事情吗?我想你打错了。search.save在末尾。你能解释一下你到底想做什么吗?另外,我不明白构建应该做什么,我认为它不会保存搜索结果。
build
+
save
很好。。但是,如果要将其切换为create,则应使用
create因为我们没有检查结果。并将其全部打包到一个事务中。事实上,我不知道build+save会起作用,谢谢!我并没有故意对create施加冲击,因为这个问题的save方法并没有受到冲击。我认为区别在于它抛出一个错误,而不是返回一个布尔结果。
query = <<-SQL
INSERT INTO search_results (search_id, donation_id)
SELECT :search_id, id
FROM donations
INNER JOIN donor AS donor.id = donation.donor_id
WHERE donors.name LIKE :query
SQL
search = Search.create(query: 'Smith')
results = Donation.joins(:donor)
                  .where('donors.name like ?', "%#{search.query}%")
                  .find_each.map do |donation|
  search.search_results.new(donation: donation)
end
results += Donation.joins(:recipient)
                   .where('recipients.name like ?', "%#{search.query}%")
                   .find_each.map do |donation|
  search.search_results.new(donation: donation)
end
SearchResult.import results