>我有 2 个大ActiveRecord::Relations
,每个都有 10 或 100 条数千条用户记录,我们称它们为ar1
和ar2
.我想对ar1
进行排序,以便ar2
中也出现的所有记录都放在ar1
的前面。
这很容易用sort_by
,但由于记录数量如此之大,在某些情况下,sort_by
需要一分钟以上才能执行。
我认为我最好的选择是尝试通过 ActiveRecord 以这种方式对记录进行排序,但我找不到任何方法来做到这一点。有没有办法使用ActiveRecord或其他方法快速完成此结果?
谢谢!
编辑:
这是我使用的排序代码:ar1.sort_by { |e| ar2.index(e) || Float::INFINITY }
我首先要说的是,你真的不需要sort_by
!
您可以通过以下方式实现相同的目标:
ar1.sort_by { |e| ar2.index(e) || Float::INFINITY }
方法类似于:
result = ((ar2 & ar1) + ar1).uniq
这要快得多。
这里有一个简单的基准来支持这个建议。
require 'benchmark'
ar1 = 100_000.times.map {|x|( 100_000_000 * rand).to_i }.uniq
ar2 = 100_000.times.map {|x|( 100_000_000 * rand).to_i }.uniq
puts ar1.size
puts ar2.size
Benchmark.bm do |x|
result_2 = []
result_1 = []
x.report('sort') do
result_2 = ar1.sort_by { |e| ar2.index(e) || Float::INFINITY }
result_2.size
end
x.report('array') do
result_1 = ((ar2 & ar1) + ar1).uniq
result_1.size
end
puts result_1.size
puts result_2.size
puts result_1 == result_2
end
给出一些不错的结果
user system total real
sort 45.287331 0.012233 45.299564 ( 45.539641)
array 0.010782 0.004000 0.014782 ( 0.014792)
我省略了一些验证输出。
现在是活动记录部分。 根据ar2
集合大小,您可以检索 id 并按该大小对第二个查询进行排序。鉴于ar1 = first_query.order(first_order)
ar2_ids = second_query.pluck(:id)
ar1 = first_query.order("FIELD(id, #{ar2_ids.join(',')}), first_order")
这将首先保留ar2
,稍后保留其他内容。 您将需要调查ar2
的大小,因为根据您的数据库,提供具有数千个 id 的订单查询可能不是最佳的。
在对这个问题进行了一些思考之后,我会选择数组操作方法来保持简单。我不会选择数据库解决方案,因为它很可能会弄乱查询的可读性,太多了。
这完全取决于您问题的细节!我希望这有所帮助。
对于您提供的信息,您可以尝试以下操作:
ids = ar2.ids
ar1.where(id: ids) + ar1.where.not(id: ids)
如果您能告诉我们更多:(例如,ar1
和ar2
有什么区别),可能会有更好的解决方案。
如果可能的话,我们应该使用 sql 查询而不是 ruby
active_users = User.where(sign_in_count: 1..) # ar1 query
inactive_users = User.where.not(id: active_users) #ar2 query
all_users = active_users | inactive_users # (ar1 + ar2).uniq
SQL:
-- query1
SELECT * FROM table1
INTERSECT
-- query2
SELECT * FROM table2
UNION
-- query1
SELECT * FROM table1
选项 1
User.connection.unprepared_statement do
User.find_by_sql """
#{User.where(query1...).to_sql}
INTERSECT
#{User.where(query2...).to_sql}
UNION
#{User.where(query1...).to_sql}
"""
end
选项 2
(User.where(query1...) & User.where(query2...)) + User.where(query1...)