4

Я хочу использовать один запрос для обновления многих записей в моей базе данных Postgres с использованием хеша или массива Ruby, вместо того чтобы выполнять итерацию по каждой записи и вызывать отдельный Обновить.Одиночный запрос 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 рубиново-фикация Клин в третьем вариант:

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 

 Смежные вопросы

  • Нет связанных вопросов^_^