4

我想使用單個查詢來使用Ruby哈希或數組更新Postgres數據庫中的很多記錄,而不是遍歷每條記錄並單獨調用更新。單個Postgres查詢使用本地哈希/數組更新許多記錄

# {:id => :color} 
my_hash = { 
    1 => 'red', 
    2 => 'blue', 
    3 => 'green' 
} 

如何,我不想這樣做,因爲它做了三個查詢:

my_hash.each do |id, color| 
    MyModel.where(id: id).update_all(color: color) 
end 

如何我也想這樣做:

MyModel.connection.execute <<-SQL 
    UPDATE my_models 
    SET color=something 
    FROM somehow_using(my_hash) 
    WHERE maybe_id=something 
SQL 

回答

2

您可以使用case

update my_models 
    set color = case id 
     when 1 then 'red' 
     when 2 then 'blue' 
     when 3 then 'green' 
    end; 

或將散列保存在一個單獨的表:

create table my_hash (id int, color text); 
insert into my_hash values 
    (1, 'red'), 
    (2, 'blue'), 
    (3, 'green'); 

update my_models m 
    set color = h.color 
    from my_hash h 
    where h.id = m.id; 

還有一個選項,如果你知道去選擇哈希爲jsonb

with hash as (
    select '{"1": "red", "2": "blue", "3": "green"}'::jsonb h 
    ) 
update my_models 
    set color = value 
    from hash, jsonb_each_text(h) 
    where key::int = id; 

OP紅寶石fying克林的第三個選項:

sql = <<-SQL 
with hash as (
    select '#{my_hash.to_json}'::jsonb h 
) 
update my_models 
    set color = value 
    from hash, jsonb_each_text(h) 
    where key::int = id; 
SQL 

ActiveRecord::Base.connection.execute(sql) 
+0

看起來像它的工作,但創建和銷燬表似乎是一個很大的開銷(是嗎?),它似乎有點笨拙做必要的文本交易:my_hash.map {| ID,色| 「當#{id}然後'#{color}'」} join(「\ n」) – LikeMaBell

+1

我明白了。事實上,我不是一個紅寶石用戶,也不知道它的能力。但是,我認爲第三種選擇可能會有用。查看更新後的答案。 – klin

0

另一個解決方案是將一系列更新連接成單個字符串並一次發送。不利的一面是它通過線路發送更多的數據,另一方面PG不需要反序列化和處理JSON。

ActiveRecord::Base.connection.execute(
    my_hash.collect{|id,color| "UPDATE my_models SET color=#{color} WHERE id=#{id};"}.join('') 
) 
# And don't forget to sanitize the :id and :color values