比较 2 个大的活动记录关系以进行排序



>我有 2 个大ActiveRecord::Relations,每个都有 10 或 100 条数千条用户记录,我们称它们为ar1ar2.我想对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)

如果您能告诉我们更多:(例如,ar1ar2有什么区别),可能会有更好的解决方案。

如果可能的话,我们应该使用 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...)

相关内容

  • 没有找到相关文章

最新更新