【问题标题】:Get differences between Array and ActiveRecord thousands of records获取 Array 和 ActiveRecord 数千条记录之间的差异
【发布时间】:2021-02-04 21:59:54
【问题描述】:

与 ActiveRecord 相比,我试图在包含大约 500,000 条记录的 csv 数据之间获取添加、删除和更新的增量。

iden 是他们差异的标识符

例如。 csv_data

[{
 iden: 1, group_num: 111 
}, 
{
 iden: 2, group_num: 222
}, 
{
 iden: 3, group_num: 333
}, 
{
 iden: 4, group_num: 444
}]

例如。活动记录数据

[{
 iden: 2, group_num: 222
}, 
{
 iden: 3, group_num: 333
}, 
{
 iden: 4, group_num: 999
}, 
{
 iden: 5, group_num: 555
}]

结果我想得到

添加的数组

[{
 iden: 5, group_num: 555
}]

删除数组

[{
 iden: 1, group_num: 111 
}]

一组更新

[{
 iden: 4, group_num: 999
}]

我尝试遍历每个以获取特定的增量,但对于数十万个大型数据集需要花费数小时。我该如何更好地优化它?

    additions = []
    updates = []
    csv_data.each_slice(1000).map do |chunk|
      chunk.map { |csv_item|
        active_record = ActiveRecordData.where(iden: csv_item[:iden])
        if !active_record.exists?
          additions << active_record
        elsif active_record.first.group_num != csv_item[:group_num]
          updates << active_record
        end
      }
    end
    deletions = ActiveRecordData.all.select{|active_record| !csv_data.any?{|csv_item| csv_item[:iden] == active_record.iden}}

【问题讨论】:

    标签: ruby-on-rails ruby


    【解决方案1】:

    我将从解决这些问题开始:

    • 每个项目的多个查询
    • 加载您不使用的数据
    • 不必要地实例化 ActiveRecord 模型
    csv_data.each_slice(1000).map do |chunk|
      records = ActiveRecordData
                  .where(iden: chunk.map(&:iden))
                  .pluck(:iden, :group_num)
    
      additions += chunk.reject do |row|
        records.find { |record| record.iden == row.iden }
      end
    
      updates += chunk.select do |row|
        record = records.find { |record| record.iden == row.iden }
        record.group_num != row.group_num
      end
    end
    

    最后,您可能需要以不同的方式处理您的deletions。如果您的iden 值是数字且相对顺序,一种简单的方法是仅获取某个范围内的iden 值(例如where(iden: 1..100_000).pluck(:iden)),然后遍历您的数据以识别并添加已删除的记录到@987654327 @buffer 在继续下一批之前。

    【讨论】:

    • 没有明确说明,但是从预期输出的例子来看,似乎OP想在相反的方向做一个diff,即需要什么改变对 CSV 数据进行处理,使其与 DB 数据匹配。这会使它变得更加困难,基本上你需要让数据库给你“不是在 CSV 中的所有东西”而不在查询中指定 CSV,因为它包含 0.5M 条目。而且您不能分块进行,因为如果当前块中没有某些内容,则它可能在下一个中。
    • 谢谢@BorisB。我没有明确说明的东西,但我试图获取 ActiveRecord 中没有但已添加到 CSV 数据中的内容。不知道如何在不调用许多查询的情况下完成它。
    • 感谢您对解决方案的详尽解释并指出我在代码中遇到的问题@coreyward!
    • @KamSami 如果您确实想按照 Boris B. 的建议进行操作,您基本上可以从另一个方向着手,并使用 find_each 批量遍历表中的所有行。跨度>
    【解决方案2】:

    我将创建一个带有ident 上的主键的临时表,并使用块中的批量插入将 CSV 数据加载到表中。一旦到达那里,获得两个表的差异将是微不足道的(而且非常快):

    SELECT table_a.ident, table_a.group_num FROM table_a WHERE table_a.ident NOT IN (SELECT table_b.ident FROM table_b)

    SELECT table_b.ident, table_b.group_num FROM table_b WHERE table_b.ident NOT IN (SELECT table_a.ident FROM table_a)

    SELECT table_a.ident, table_a.group_num INNER JOIN table_b ON table_a.ident = table_b.ident AND table_a.group_num &lt;&gt; table_b.group_num

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-06-29
      • 2011-05-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多