代码之家  ›  专栏  ›  技术社区  ›  DanSingerman

从哈希更新许多行,而不只是在Rails中循环

  •  1
  • DanSingerman  · 技术社区  · 16 年前

    我有一个哈希,它有超过1000个键/值对。

    我有一个数据库表,有几千行。

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

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

    但这将完成1000多个SQL更新语句。

    (在Rails中)有没有好的方法可以用一个(或几个)更新语句来完成这项工作?

    4 回复  |  直到 16 年前
        1
  •  3
  •   ttarchala    16 年前

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

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

        2
  •  2
  •   Mike Woodhouse    16 年前

    我不确定“好”,但在许多DBMS平台上,可以在一次执行中发送多个SQL语句。这个 ar-extensions 例如,gem可以帮助批处理insert语句,尽管根据平台的不同,它可能需要进行一些调整——例如,Oracle有点棘手。我不认为它直接处理更新,但是您可以从这里的另一个答案来研究使用临时表技巧:批量加载数据并使用 ActiveRecord.execute .

    可能只有在可能的情况下才是“好的”,但是Rails(或者更准确地说是ActiveRecord)从未打算绝对地使用ORM函数 一切 这就是为什么 find_by_sql 存在:当你知道更好的方法的时候,他们就在那里。

        3
  •  2
  •   DanSingerman    16 年前

    好吧,我已经用迈克·伍德豪斯和特塔卡拉的暗示解决了这个问题。这是代码(我正在使用 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可能有所不同。

        4
  •  -1
  •   Jeff Ober    16 年前

    对。我不知道Rails的具体情况,但是您可以使用in-sql关键字,其工作方式如下:

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