8

假设我想进行批量更新,为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 查询中执行这三个更新?

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

  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
    

    这使得使用单个查询成为可能,但使查询变得丑陋且效率低下(尤其是对于混合和/或复杂的数据类型)。

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

4

1 回答 1

8

通常,您希望从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
     ;

id请记住:字段中的索引(或主键)updates_table很重要。(但对于像这样的小集合,优化器可能会选择一个哈希连接)


另外:对于更新,重要的是要避免使用相同的值进行更新,这会导致创建额外的行版本 + 加上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)
            ;
于 2015-02-25T15:57:52.197 回答