Postgresql中的批量更新

时间:2013-06-21 22:32:20

标签: java sql postgresql jdbc spring-jdbc

我在Postgres中需要UPSERT功能。由于Postgres不支持本机,我编写了一个函数来执行该操作(尝试更新,如果没有更新行则插入)

这是该功能的模板:https://stackoverflow.com/a/1109198/681671

CREATE FUNCTION merge_db(key INT, data TEXT) RETURNS VOID AS
$$
BEGIN
    LOOP
        -- first try to update the key
        UPDATE db SET b = data WHERE a = key;
        IF found THEN
            RETURN;
        END IF;
        -- not there, so try to insert the key
        -- if someone else inserts the same key concurrently,
        -- we could get a unique-key failure
        BEGIN
            INSERT INTO db(a,b) VALUES (key, data);
            RETURN;
        EXCEPTION WHEN unique_violation THEN
            -- do nothing, and loop to try the UPDATE again
        END;
    END LOOP;
END;
$$
LANGUAGE plpgsql;


SELECT merge_db(1, 'david');

我正在使用Spring JDBC模板。这个select语句(以其参数化形式)和一个对象数组是我传递给JDBCTemplate的batchUpdate方法的。

我得到了这个例外:

 org.postgresql.util.PSQLException: A result was returned when none was expected.

我怀疑是因为使用了SELECT

我知道我可以在循环中使用Callable但这会使应用程序非常繁琐,而I / O延迟会使它变得非常慢。

如何使用Spring JDBC /原始JDBC在Postgres中完成批量upsert?

我正在使用Postgresql 9.1。

2 个答案:

答案 0 :(得分:7)

批处理upsert在PostgreSQL中完成:

  • 开始交易
  • 创建TEMPORARY表格
  • 使用JDBC批处理INSERT或(最好)使用PgJDBC驱动程序提供的COPY API填充它
  • LOCK真实目的地表IN EXCLUSIVE MODE,仅允许其他交易的SELECT继续。
  • 执行UPDATE ... FROM更新现有行
  • 执行INSERT INTO ... SELECT ... WHERE NOT EXISTS (SELECT 1 FROM real_table WHERE ...)添加尚未包含在real_table
  • 中的行
  • COMMIT交易

如果多个事务尝试执行此操作,则会在表锁上进行序列化。 upsert永远不会是一个兼容并发的操作。

答案 1 :(得分:1)

特别不了解postgres,但我通常的方法如下:

  • 执行插入
  • 如果使用unique_violation失败,请执行更新

我这样做是为了最大限度地减少对桌子的锁定,并防止竞争条件。

当然,只有在您插入的表对主键以外的列具有唯一约束时,它才会起作用。