使用本地散列/数组更新多个记录的单个 Postgres 查询

Lik*_*ell 6 postgresql sanitization ruby-on-rails query-optimization sql-update

我想使用单个查询来使用 Ruby 哈希或数组更新 Postgres 数据库中的许多记录,而不必遍历每条记录并调用单独的更新。

# {:id => :color}
my_hash = {
  1 => 'red',
  2 => 'blue',
  3 => 'green'
}
Run Code Online (Sandbox Code Playgroud)

我不想这样做,因为它执行三个串行查询:

my_hash.each do |id, color|
  MyModel.where(id: id).update_all(color: color)
end
Run Code Online (Sandbox Code Playgroud)

我想怎么做:

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

kli*_*lin 2

您可以使用case

update my_models
    set color = case id
        when 1 then 'red'
        when 2 then 'blue'
        when 3 then 'green'
    end;
Run Code Online (Sandbox Code Playgroud)

或者将哈希值保存在单独的表中:

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;
Run Code Online (Sandbox Code Playgroud)

如果您知道选择哈希的方法,还有一种选择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;
Run Code Online (Sandbox Code Playgroud)

OP ruby​​-fying klin 的第三个选项:

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)
Run Code Online (Sandbox Code Playgroud)