rails new xyz
cd xyz
rails g scaffold Donor name
rails g scaffold Recipient name
rails g scaffold Donation amount:integer donor:references recipient:references
rails g scaffold Search query
rails g model SearchResult search:references donation:references
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中使用RAW SQL的忠实拥护者,但是如果有一种方法可以在纯SQL中解决该问题,那会更快,那也可能。
答案 0 :(得分:1)
正如@matthewd所指出的那样,建立关联记录并保存父级实际上是可行的
您建议的代码可能存在问题。实际上,活动记录的构建方法不会像您期望的那样持久保存搜索结果:http://guides.rubyonrails.org/association_basics.html#methods-added-by-has-many-collection-build-attributes
一种正确的持久存储方式是:
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
当然,正如您所指出的那样,它根本没有效率,有两种解决方法。或手工制作一个名为https://github.com/zdennis/activerecord-import的炫酷宝石
这不是推荐的方法,但是我在这里提供了一些信息。 这是您可以使用的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
您可以使用ActiveRecord::Base.connection.execute
方法来启动它,但这也意味着您需要自己清理查询。我可以走这条路,但让我们继续研究另一种我认为更安全,更易于维护的解决方案。
https://github.com/zdennis/activerecord-import
您可以使用此代码
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
注意一些重要的事情:
希望这会很有用!