3

我需要UPSERTPostgres 中的功能。由于 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 模板。这个选择语句(以其参数化形式)和一个对象数组是我传递给 JDBCTemplate 的 batchUpdate 方法的。

我得到这个例外:

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

我怀疑这是因为使用SELECT.

我知道我可以在循环中使用 Callable ,但这会使应用程序变得非常健谈,并且 I/O 延迟会使其非常慢。

如何使用 Spring JDBC / raw JDBC 在 Postgres 中完成批量更新插入?

我正在使用 Postgresql 9.1。

4

2 回答 2

7

批量更新插入在 PostgreSQL 中通过以下方式完成:

  • 开始交易
  • 创建TEMPORARY
  • 使用 JDBC 批处理INSERT或(最好)使用COPYPgJDBC 驱动程序提供的 API 填充它
  • LOCK真正的目标表IN EXCLUSIVE MODE,它只允许SELECT来自其他事务的 s 继续。
  • 执行UPDATE ... FROM更新现有行
  • 执行INSERT INTO ... SELECT ... WHERE NOT EXISTS (SELECT 1 FROM real_table WHERE ...)添加尚未添加的行real_table
  • COMMIT交易

如果多个事务尝试执行此操作,它们将在表锁上获得序列化。upsert 永远不会是并发友好的操作。

于 2013-06-22T10:37:05.340 回答
1

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

  • 做插入
  • 如果它因 unique_violation 失败,请进行更新

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

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

于 2013-06-21T22:58:38.407 回答