ruby-on-rails 在循环中销毁记录会产生有趣的行为

vsdwdz23  于 2023-03-31  发布在  Ruby
关注(0)|答案(2)|浏览(125)

我试图删除表中重复的记录。我的目标是总是删除旧的记录。这是一个rake任务。
主要部分与下图相似,

TableName.order(updated_at: :asc).each do |record|
      next if record.valid?
      record.destroy!
    end

如果存在2个以上的重复行,则此循环仅删除其中一个并存在。
为了理解它,我调试了循环并逐行观察,嗒哒!一切正常。可能在销毁!操作没有执行之前循环迭代。可能,它只删除最后一条记录(不是每个重复组的最后一项)
无论如何,我可以通过生成一个数组来一次性销毁所有数据或smth来修复它,但我很好奇为什么ruby/ActiveRecord会这样做。
作为回答,请详细解释发生了什么。谢谢🙌🏻🙌🏻🙌🏻

yftpprvb

yftpprvb1#

一旦调用record.destroy!,其他重复记录的updated_at属性不会更新,因此它们在数据库中保持当前顺序。因此,循环只能删除遇到的第一个重复记录,而不能删除其他记录。

7uhlpewt

7uhlpewt2#

您遇到的问题似乎与修改正在迭代的集合有关。当您在迭代期间销毁记录时,集合会被修改,迭代器会丢失其位置,可能会跳过某些元素。

这不是Ruby或ActiveRecord特有的,而是一个通用的编程概念。

在Ruby中,当你迭代一个集合时,迭代器会保留一个当前位置的内部索引。如果集合被修改(例如,一个元素被删除),索引可能指向一个不正确的位置,导致意外的行为。
要解决此问题,您可以在迭代期间收集要销毁的记录的ID,然后在循环结束后一次性销毁它们。下面是一个示例:

duplicate_record_ids = []

TableName.order(updated_at: :asc).each do |record|
  next if record.valid?
  duplicate_record_ids << record.id
end

TableName.where(id: duplicate_record_ids).destroy_all

这段代码首先收集数组duplicate_record_ids中无效记录的ID。循环结束后,它使用where查询获取所有具有这些ID的记录,并在一个批处理中销毁它们。
通过将ID集合与实际删除分离,可以避免在迭代集合时修改集合,从而避免任何意外行为。
希望这个有用。

相关问题