对于所有Ruby高尔夫球手和体操运动员来说,这应该是一个有趣的小谜题。
问题:我有数以百万计的SQL数据库记录,我需要以反向ID顺序批量转移到NoSql数据库(大约1000个左右)(因此find_each不是有效的解决方案)。
这将在小型服务器上运行,因此我不想一次将所有ID保留在内存中,只需要一个块的ID。
我希望在我的代码库中使用这些内容:
chunkify max_id, step_size do |ids|
copy_to_nosql SqlTable.where(id: ids)
end
你能写chunkify
吗?
解决方案不应重复任何ID,它应覆盖所有ID,第一个产生的数组应包含max_id,返回的最低ID应为1.
PS。我正在使用Rails框架,所以请随意使用Rails特定的语言扩充。
PPS。我不想使用像'limit'和'offset'这样的数据库驱动的解决方案,至少我的数据库实现,随着偏移的增加,查询需要更长的时间来处理。
修改
这是一个有效的解决方案。任何提高可读性,简洁性或效率的替代方案都是最受欢迎的:
# yields arrays of ints (chunks), of [chunk_size] or lower length, which,
# when added together, would cover all values from 0 to [top_value], exactly
# once each. The highest value 'chunk' is provided first.
#
# > chunkify(100, 10) { |chunk| puts chunk.inspect }
# [91, 92, 93, 94, 95, 96, 97, 98, 99, 100]
# [81, 82, 83, 84, 85, 86, 87, 88, 89, 90]
# ...
# [11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
# [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# => 100
# > chunkify(27, 8) { |chunk| puts chunk.inspect }
# [20, 21, 22, 23, 24, 25, 26, 27]
# [12, 13, 14, 15, 16, 17, 18, 19]
# [4, 5, 6, 7, 8, 9, 10, 11]
# [1, 2, 3]
# => 27
def chunkify(top_value, chunk_size)
top_value.step(1, -chunk_size) do |i|
yield ((i-chunk_size > 0 ? i-chunk_size+1 : 1)...i+1).to_a
end
end
答案 0 :(得分:2)
你在寻找像这样简单的东西吗?
def chunkify(max_val,chunk_size);
max_val.downto(1).each_slice(chunk_size) {|chunk| yield chunk.reverse }
end
chunkify(100,10) {|c| puts c.inspect }
#[91, 92, 93, 94, 95, 96, 97, 98, 99, 100]
#[81, 82, 83, 84, 85, 86, 87, 88, 89, 90]
#[71, 72, 73, 74, 75, 76, 77, 78, 79, 80]
#[61, 62, 63, 64, 65, 66, 67, 68, 69, 70]
#[51, 52, 53, 54, 55, 56, 57, 58, 59, 60]
#[41, 42, 43, 44, 45, 46, 47, 48, 49, 50]
#[31, 32, 33, 34, 35, 36, 37, 38, 39, 40]
#[21, 22, 23, 24, 25, 26, 27, 28, 29, 30]
#[11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
#[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
#=> nil
正如@SergioTulentsev所指出的那样,这似乎微不足道。
反向效率也会降低,与递减顺序一样有效,因为SQL不关心IN()
子句的顺序。
答案 1 :(得分:1)
ActiveRecord提供了一种find_each
方法,可用于加载小批量记录并对结果集执行特定操作。
循环遍历数据库中的一组记录(例如,使用all方法)是非常低效的,因为它会尝试一次实例化所有对象。
在这种情况下,批处理方法允许您批量处理记录,从而大大减少内存消耗。
find_each
方法使用find_in_batches
批量大小为1000
(或:batch_size
选项指定)。
您可能希望向要迁移的列添加新的布尔字段,以确保将查询过滤到仅processed
为false的记录。这样,即使进程由于某种原因而死亡,您也可以恢复它,并且不会从头开始重启。