从散列中更新许多行而不只是在Rails中循环

时间:2009-10-19 12:12:41

标签: sql ruby-on-rails

我有一个超过一千个键/值对的哈希。

我有一个包含数千行的数据库表。

因此,基于哈希来强制更新表非常简单。例如:

my_hash.each{|key,value|
   Model.update_all("column2 = #{value}", "column1 = #{key}")
}

但是这将会有超过一千个SQL更新语句。

有一个好的方法(在Rails中)用一个(或只是几个)更新语句来做这个吗?

4 个答案:

答案 0 :(得分:3)

最快的方法 - 在任何语言中,都不知道它是如何在Rails中完成的 - 将内存中的哈希值转储到数据库中的临时表中,该表只包含哈希键的列和值,然后发出一个带有连接的UPDATE命令,该连接将要更新的表与新的临时表链接。

检查查询计划以确保优化程序正常工作,在发布更新之前在临时表上构建临时索引。如果没有,请自行构建索引。

答案 1 :(得分:2)

我不确定“好”,但在许多DBMS平台上,可以在一次执行中发送多个SQL语句。例如,ar-extensions gem可以帮助批处理INSERT语句,尽管根据平台可能需要稍微调整一下 - 例如,Oracle有点棘手。我不认为它直接处理UPDATE,但您可以在此处查看另一个答案中的临时表技巧:批量加载数据并使用ActiveRecord.execute运行更新。

可能只有“好”才可能,但Rails(或更确切地说是ActiveRecord)从未打算将ORM函数用于绝对所有,这就是为什么find_by_sql存在:当你知道更好的方式时,它们就在那里。

答案 2 :(得分:2)

我已经用Mike Woodhouse和ttarchala的提示解决了这个问题。这是代码(我正在使用Ruby Sequel gem

# connect to the database
DB = Sequel.connect("postgres://user:password@localhost/blah")

# create the temporary table
DB.create_table :temp_update, :temp => true do
  primary_key :key
  String :value
end

# insert rows from the hash (multi_insert takes an array of hashes,hence the map)
DB[:temp_update].multi_insert(my_hash.map{|k,v| {:key => k, :value => v}})

# Do the update based on a join
DB.execute("  UPDATE my_table 
              SET column2 = temp_update.value 
              FROM temp_update
              WHERE column1 = temp_update.id")

我正在使用Postgres,因此对于其他RDBMS,确切的更新SQL可能会有所不同。

答案 3 :(得分:-1)

是。我不是特别了解rails,但你可以使用IN sql关键字,其工作方式如下:

select * from table where column in ('list', 'of', 'values')