在ruby中解析大型txt文件需要花费大量时间?

her*_*y92 0 ruby

下面是从互联网上下载大约9000行的txt文件并填充数据库的代码,我已经尝试了很多,但需要花费大量时间超过7分钟.我使用win 7 64 bit和ruby 1.9.3.有没有办法更快地做到这一点?

require 'open-uri'
require 'dbi'
dbh = DBI.connect("DBI:Mysql:mfmodel:localhost","root","")
#file = open('http://www.amfiindia.com/spages/NAV0.txt')
file = File.open('test.txt','r')
lines = file.lines
2.times { lines.next }
curSubType = ''
curType = ''
curCompName = ''
lines.each do |line|
    line.strip!
if line[-1] == ')'
    curType,curSubType = line.split('(')
    curSubType.chop!
elsif line[-4..-1] == 'Fund'
    curCompName = line.split(" Mutual Fund")[0] 
elsif line == ''
    next
else
    sCode,isin_div,isin_re,sName,nav,rePrice,salePrice,date = line.split(';')
    sCode = Integer(sCode)
    sth = dbh.prepare "call mfmodel.populate(?,?,?,?,?,?,?)"
    sth.execute curCompName,curSubType,curType,sCode,isin_div,isin_re,sName
end
end
dbh.do "commit"
dbh.disconnect
file.close


106799;-;-;HDFC ARBITRAGE FUND RETAIL PLAN DIVIDEND OPTION;10.352;10.3;10.352;29-Jun-2012
Run Code Online (Sandbox Code Playgroud)

这是要插入表中的数据格式.现在有8000条这样的行,如何通过组合所有这些来执行插入并仅调用一次该过程.另外,mysql是否支持数组和迭代在例程中做这样的事情.请提出你的建议.谢谢.

编辑

我必须根据它们是否已经存在而对表格进行插入,我还需要在插入表格之前使用条件比较.我绝对不能为这些编写SQL语句,所以我编写了SQL存储过程.现在我有一个@the_data列表,如何将其传递给过程,然后在MySQL端迭代它.有任何想法吗 ?

insert into mfmodel.company_masters (company_name) values
#{@the_data.map {|str| "('#{str[0]}')"}.join(',')}
Run Code Online (Sandbox Code Playgroud)

这使得100个插入,但其中35个是冗余的,所以我需要在插入之前在表中搜索现有条目.

有任何想法吗 ?谢谢

Ale*_*x D 5

从您的评论中,您似乎花费了所有时间来执行数据库查询.在最近的Ruby项目中,我还必须优化一些慢速代码,这些代码将CSV文件中的数据导入数据库.通过使用单个批量INSERT查询导入所有数据,而不是对CSV文件的每一行进行1次查询,我的性能提高了500倍.我在数组中累积了所有数据,然后使用字符串插值构建了一个SQL查询Array#join.

从您的评论中,您似乎可能不知道如何为批量构建和执行动态SQL INSERT.首先在嵌套数组中获取数据,并以已知顺序插入字段.举个例子,假设我们有这样的数据:

some_data = [['106799', 'HDFC FUND'], ['112933', 'SOME OTHER FUND']]
Run Code Online (Sandbox Code Playgroud)

您似乎使用Rails和MySQL,因此动态SQL必须使用MySQL语法.要构建和执行INSERT,您可以执行以下操作:

ActiveRecord::Base.connection.execute(<<SQL)
  INSERT INTO some_table (a_column, another_column)
  VALUES #{some_data.map { |num,str| "(#{num},'#{str}')" }.join(',')};
SQL
Run Code Online (Sandbox Code Playgroud)

您说您需要将数据插入2个不同的表中.那不是问题; 只是在不同的数组中累积每个表的数据,并执行2个动态查询,可能在事务内部.2个查询将比9000快得多.

再次,您在评论中说,您可能需要更新一些记录而不是插入.我在上面提到的"CSV导入"案例中也是这种情况.解决方案只是稍微复杂一点:

# sometimes code speaks more eloquently than prose
require 'set'
already_imported = Set.new
MyModel.select("unique_column_which_also_appears_in_imported_files").each do |x|
  already_imported << x.unique_column_which_also_appears_in_imported_files
end

to_insert,to_update = [],[]
imported_data.each do |row|
  # for the following line, don't let different data types 
  #   (like String vs. Numeric) get ya
  # if you need to convert the imported data to match correctly against what's
  #   already in the DB, do it!
  if already_imported.include? row[index_of_unique_column]
    to_update << row
  else
    to_insert << row
  end
end
Run Code Online (Sandbox Code Playgroud)

然后,您必须为所涉及的每个表构建动态INSERT 动态UPDATE.谷歌的UPDATE语法,如果你需要它,并疯狂与所有你喜欢的字符串处理功能!

回到上面的示例代码,请注意数字和字符串字段之间的区别.如果字符串可能包含单引号,则必须确保所有单引号都被转义.String#gsub当你尝试这样做时,你的行为可能会让你感到惊讶:它赋予了特殊的意义\'.到目前为止,我发现逃避单引号的最佳方法是:string.gsub("'") { "\\'" }.也许其他海报知道更好的方式.

如果要插入日期,请​​确保它们已转换为MySQL的日期语法.

是的,我知道"自己动手"的SQL清理是非常不确定的.上述方法甚至可能存在安全漏洞; 如果是这样,我希望我的消息灵通的同行会让我直截了当.但性能提升太大了,不容忽视.再次,如果这可以使用带有占位符的准备好的查询来完成,并且您知道如何,请发布!

查看代码,看起来您正在使用存储过程(mfmodel.populate)插入数据.即使您确实想要使用存储过程,为什么还要进行dbh.prepare循环?你应该能够将那条线移到外面lines.each.