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

时间:2015-02-25 15:43:37

标签: postgresql sql-update bulkupdate

假设我要进行批量更新,为 a 值的集合设置 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

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

[ [ 1, 'foo' ]
  [ 2, 'bar' ]
  [ 3, 'baz' ] ]

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

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

  1. 临时表

    CREATE TABLE temp ...;
    INSERT INTO temp (id,value) VALUES (....);
    UPDATE foo USING temp ...
    

    但这确实只是解决了问题。尽管进行批量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
    

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

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

1 个答案:

答案 0 :(得分: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
        ;

所以你应该通过以下方式填充你的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
     ;

请记住:idupdates_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)
            ;