Mysql 更新元素后获取查询集计数的最佳方法
我正在寻找在rails中获取查询集大小的最佳方法。但是,元素是在循环中更新的,我需要更新之前的元素计数。这里是一些例子马车!代码Mysql 更新元素后获取查询集计数的最佳方法,mysql,ruby-on-rails,ruby,Mysql,Ruby On Rails,Ruby,我正在寻找在rails中获取查询集大小的最佳方法。但是,元素是在循环中更新的,我需要更新之前的元素计数。这里是一些例子马车!代码 p = participations.where(invited_at: nil).limit(50) p.each do |participation| # Invite may raise an exception, but also contains operations that # cannot be undone participatio
p = participations.where(invited_at: nil).limit(50)
p.each do |participation|
# Invite may raise an exception, but also contains operations that
# cannot be undone
participation.invite()
participation.invited_at = Time.zone.now
participation.save
end
DoStuff() if p.count > 0
此代码不起作用,因为P.CULL中的调用创建了一个新的数据库查询,该查询不考虑在循环中已更新的记录。因此,如果记录少于50条,则所有记录都会更新,并且不会调用DoStuff
rails中最惯用的处理方法是什么: 将if p.count部分移出循环,并且仅在有任何记录时才进入循环? 将p.count替换为p.size如果我正确理解大小,这不会导致任何额外的查询 计算循环中的迭代次数,然后使用该次数 我觉得1在ruby中是最地道的,但我在这门语言中没有太多经验 编辑:改进示例,使其更接近原始代码 编辑:问题不在于对循环中的参与者执行的更新查询。这些查询应该是单独的查询,以跟踪哪些参与已被处理,即使出现错误也是如此。相反,问题在于,只要循环中有任何处理过的记录,就应该调用DoStuff。但是,由于count在处理记录后执行新查询,因此如果要处理的元素少于50个,则所有元素都将更新,并且不会调用DoStuff。这就是count(始终执行查询)和size(如果加载对象,将返回加载对象的数量)之间的差异,否则,我们将退回计数。因此,最简单的修复方法是用大小替换计数 但是每个都返回它迭代的集合,因此如果p.each&block.any?如果有多行块,看起来就不好看了 一种更简洁的方法是将代码封装在方法中,并添加一个guard子句,这样一来,审阅者就不必知道大小和计数之间的差异,也不必检查每个元素是否产生了一个至少包含一个元素的集合
def process_invitations
p = participations.where(invited_at: nil).limit(50)
return if p.none?
p.each do |participation|
# Invite may raise an exception, but also contains operations that
# cannot be undone
participation.invite()
participation.invited_at = Time.zone.now
participation.save
end
DoStuff()
end
您甚至可以取消限制并使用p.first50.do
将if p.count部分移出循环,并且仅在有任何记录时才进入循环
。如果没有记录,则每个都不会进入循环。如果您想要验证,请尝试:
MyModel.none.each { puts "Hello world" }
如果你想要一种更惯用的方法,那么如果你关心结果,就不要使用每种方法。只有当您只关心迭代的副作用时,才应该使用每种方法
取而代之的是使用map或其他许多迭代方法中的一种
def process_invitations
p = participations.where(invited_at: nil).limit(50)
p.map do |participation|
# Invite may raise an exception, but also contains operations that
# cannot be undone
participation.invite()
participation.invited_at = Time.zone.now
participation.save
end.reject.yeild_self do |updates|
# run do_stuff if any of the records where updated
do_stuff() if updates.any?
end
end
或者,如果您只想对更新的记录执行以下操作:
def process_invitations
p = participations.where(invited_at: nil).limit(50)
p.map do |participation|
# Invite may raise an exception, but also contains operations that
# cannot be undone
participation.invite()
participation.invited_at = Time.zone.now
participation if participation.save
end.reject.yeild_self do |updated|
do_stuff(updated) if updated.any?
end
end
def process_invitations
p = participations.where(invited_at: nil).limit(50)
p.map do |participation|
# Invite may raise an exception, but also contains operations that
# cannot be undone
participation.invite()
participation.invited_at = Time.zone.now
participation if participation.save
end.reject.map do |record|
do_stuff(record)
end
end
或者对更新的每个记录执行以下操作:
def process_invitations
p = participations.where(invited_at: nil).limit(50)
p.map do |participation|
# Invite may raise an exception, but also contains operations that
# cannot be undone
participation.invite()
participation.invited_at = Time.zone.now
participation if participation.save
end.reject.yeild_self do |updated|
do_stuff(updated) if updated.any?
end
end
def process_invitations
p = participations.where(invited_at: nil).limit(50)
p.map do |participation|
# Invite may raise an exception, but also contains operations that
# cannot be undone
participation.invite()
participation.invited_at = Time.zone.now
participation if participation.save
end.reject.map do |record|
do_stuff(record)
end
end
但是,由于count在处理记录后执行新查询,…,所以使用size,否?p已经在内存中,如果没有任何更改它的值,那么DoStuff的使用不应该受到影响。