使用成对输入进行批量更新的最有效方法

Fli*_*mzy 3 postgresql sql-update bulkupdate

假设我想做一个批量更新,设置A = B的集合一个值.这可以通过一系列UPDATE查询轻松完成:

UPDATE foo SET value='foo' WHERE id=1
UPDATE foo SET value='bar' WHERE id=2
UPDATE foo SET value='baz' WHERE id=3
Run Code Online (Sandbox Code Playgroud)

但现在我想我想要批量做这件事.我有一个包含id和新值的二维数组:

[ [ 1, 'foo' ]
  [ 2, 'bar' ]
  [ 3, 'baz' ] ]
Run Code Online (Sandbox Code Playgroud)

有没有一种有效的方法在单个SQL查询中执行这三个UPDATE?

我考虑过的一些解决方案:

  1. 临时表

    CREATE TABLE temp ...;
    INSERT INTO temp (id,value) VALUES (....);
    UPDATE foo USING temp ...
    
    Run Code Online (Sandbox Code Playgroud)

    但这真的只会解决问题.尽管进行批量INSERT可能更容易(或至少不那么难看),但仍然至少有三个查询.

  2. 通过将数据对作为SQL数组传递来对输入进行非规范化.但这使得查询非常难看

    UPDATE foo
    USING (
        SELECT
            split_part(x,',',1)::INT AS id,
            split_part(x,',',2)::VARCHAR AS value
        FROM (
            SELECT UNNEST(ARRAY['1,foo','2,bar','3,baz']) AS x
        ) AS x;
    )
    SET value=x.value WHERE id=x.id
    
    Run Code Online (Sandbox Code Playgroud)

    这使得可以使用单个查询,但使查询变得丑陋且效率低下(特别是对于混合和/或复杂数据类型).

有更好的解决方案吗?或者我应该求助于多个UPDATE查询?

wil*_*ser 5

通常,您希望从table具有足够索引的批量更新中轻松进行合并:

CREATE TEMP TABLE updates_table
        ( id integer not null primary key
        , val varchar
        );
INSERT into updates_table(id, val) VALUES
 ( 1, 'foo' ) ,( 2, 'bar' ) ,( 3, 'baz' )
        ;

UPDATE target_table t
SET value = u.val
FROM updates_table u
WHERE t.id = u.id
        ;
Run Code Online (Sandbox Code Playgroud)

所以你应该通过以下方式填充你的update_table:


INSERT into updates_table(id, val)
SELECT
        split_part(x,',',1)::INT AS id,
        split_part(x,',',2)::VARCHAR AS value
    FROM (
        SELECT UNNEST(ARRAY['1,foo','2,bar','3,baz']) 
         ) AS x
     ;
Run Code Online (Sandbox Code Playgroud)

请记住:该id字段中的索引(或主键)updates_table很重要.(但对于像这样的小集合,可能由优化器选择hashjoin)


此外:对于更新,重要的是避免使用相同的值进行更新,这会导致创建额外的rowversions +以及在VACUUM提交更新后生成的活动:

UPDATE target_table t
    SET value = u.val
    FROM updates_table u
    WHERE t.id = u.id
    AND (t.value IS NULL OR t.value <> u.value)
            ;
Run Code Online (Sandbox Code Playgroud)