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) {|...|