Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Single Postgres query to update many records using a local hash/array

I want to use a single query to update many records in my Postgres database using a Ruby hash or array, rather than having to iterate through each record and call a separate update.

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

How I don't want to do it because it does three serial queries:

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

How I do want to do it:

MyModel.connection.execute <<-SQL
  UPDATE my_models
    SET color=something
    FROM somehow_using(my_hash)
    WHERE maybe_id=something
SQL
like image 537
LikeMaBell Avatar asked Dec 18 '15 00:12

LikeMaBell


Video Answer


1 Answers

You can use case:

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

or save the hash in a separate table:

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;

One more option, if you know the way to select the hash as 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 ruby-fying klin's third option:

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)
like image 83
klin Avatar answered Sep 30 '22 12:09

klin