Sql 如何加速这段代码?

Sql 如何加速这段代码?,sql,ruby-on-rails,ruby,ruby-on-rails-3,Sql,Ruby On Rails,Ruby,Ruby On Rails 3,我有很多重复的记录,我正试图清除这些记录,为此,我目前正在运行以下程序: Survey.active.each do |survey| survey.response_sets.completed.each do |set| answer_ids = [] set.responses.each do |r| if r.answer.blank? r.destroy else if answer_ids.include?

我有很多重复的记录,我正试图清除这些记录,为此,我目前正在运行以下程序:

Survey.active.each do |survey|
  survey.response_sets.completed.each do |set|
    answer_ids = []
    set.responses.each do |r|
      if r.answer.blank?
        r.destroy
      else
        if answer_ids.include? r.answer_id
          r.destroy
        else
          answer_ids << r.answer_id
        end
      end
    end
  end
end

我正在运行Rails 3.0.6和PostgreSQL。

我认为您可能从错误的角度对此进行了攻击。您不应该首先允许坏数据进入数据库。我真的看不出您的数据库模型是什么样子,但是模型中的一些验证可能会阻止您像这样清理数据库。在Rails中加载非常大的数据集是一件痛苦的事情,而且速度非常慢,内存非常紧张

# maybe something like this?
class Responses < ActiveRecord::Base
  validates_uniqueness_of :answer_id, :scope => :id
end

如果只需要运行一次,有什么问题?如果这是一项日常任务,您可以使用后台作业来处理该任务,请查看延迟的作业或重新生成gems

但是你可以做几件事。您是否将答案包括在范围内?或使用调查。活动。包括:答案

对于AR模型,还有一种称为find_each的方法,该方法在处理大型数据集时应该更快


希望这能有所帮助。

这里只是一个想法:您确定WHERE子句中使用的字段已编制索引吗

这纯粹是SQL问题,而不是Rails问题,我是Rails n00b:,但是

response_sets.survey_id, 
response_sets.completed_at 
responses.response_set_id

如果您谈论的是几百行的数据集,那么它们肯定都应该设置索引。

我认为最好使用SQL来解决这个问题,而不是在ruby中迭代每条记录

当您需要执行此类操作时,SQL仍然是一个强大的工具

#Delete responses that do not have a corresponding answer
#AND delete responses that have a duplicate answer_id keeping only one response for each answer_id
ActiveRecord::Base.execute <<-SQL
  DELETE FROM responses
  WHERE (responses.answer_id IS NULL) OR
  (
    responses.id NOT IN (
      -- build a list of the response ids you want to keep
      SELECT responses.id
      FROM responses
      INNER LEFT JOIN 
      (
        -- get a list of responses with a unique answer id
        SELECT DISTINCT responses.answer_id
        FROM responses
      )
      -- join responses to itself on the unique list of answer ids
      -- keeping only a single record for each answer id
      as answer_ids ON responses.answer_id = answer_ids.answer_id
    )
  )
SQL

注意:我还没有测试过这个,我建议先在测试环境中运行它。

也许可以根据答案id对结果进行分组,然后只选择那些计数大于1的结果

它可以是这样的:

survey.response_sets.completed.all(
  :group_by => "answer_id",
  :select => "id, answer_id, COUNT(*) AS count_duplicates",
  :conditions => "count_duplicates > 1")
然后检查所有这些答案并销毁除第一个以外的所有答案:

duplicate_sets.group_by(:answer_id) {|...|
这将为您提供一个按每个答案id分组的所有id的数组。只需除去第一个元素,销毁其余元素

我不确定你的模型,所以我把其余的留给你。但它应该在实际操作数据之前为您提供如何准备数据的线索。我的代码也没有选择答案_id为空,但在第二次运行时应该很容易发现这些答案


确保将所有内容都打包到事务中,以便在准备和消除重复的过程中不会更改数据。

只是一个一般的SQL提示,问问自己是否确实需要对所有查询执行SELECT*?不幸的是,我不知道任何Ruby或RoR可以进一步帮助您:您可以尝试使用散列来跟踪您已经看到的答案ID,而不是数组。您可能还想尝试添加一些include并使用find_each,而不是在最外层的循环中使用each,否则您将保留以前查看的所有对象驻留在ram中。显然,要确保在所有适当的列上都有索引。更改r.答案。空白?to r.answer\u id.blank将保存大量查询,但如果没有外键,则意味着您无法捕获“悬空”答案\u id请尝试在事务调查中包装整个扣杀。事务完成…结束。这可以让Postgresql运行得更快。另外,看看是否可以使用psql直接驱动postgres进行清理,绕过rails/activerecord。我已经修复了导致重复的问题,所以我肯定是从正确的角度进行攻击的好的,错过了你正在寻找的一次性修复,添加了一个快速的小解决方案,以最大限度地减少内存开销,这可能是减慢运行速度的原因。是的,我非常确定我已经涵盖了索引。我认为唯一值得做的另一件事是这些查询的一些计时指标,看看最大的时间点球在哪里。在Postgres客户机中手动执行一些查询并没有什么坏处,可以查看您从数据库中获得的性能……这至少可以告诉您Rails代码或SQL中是否取得了巨大的成功,并且可能会让这些类型有更多的进展。。。!
survey.response_sets.completed.all(
  :group_by => "answer_id",
  :select => "id, answer_id, COUNT(*) AS count_duplicates",
  :conditions => "count_duplicates > 1")
duplicate_sets.group_by(:answer_id) {|...|