2013-06-21 3 views
3

Мне нужно UPSERT Функциональность в Postgres. Поскольку Postgres не поддерживает это изначально, я написал функцию, которая делает это (пытается обновить, если ни одна строка не была обновлена ​​затем вставляет)Пакетное обновление в Postgresql

Это шаблон для функции: 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 (в его параметризованной форме) и массив объектов - это то, что я передаю методу batchUpdate для JDBCTemplate.

Я получаю это исключение:

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

Я подозреваю, что это из-за использования SELECT.

Я знаю, что могу использовать Callable в цикле, но это сделает приложение очень частым, а латентность ввода-вывода сделает его очень медленным.

Как выполняется пакетное обновление в Postgres с использованием Spring JDBC/raw JDBC?

Я использую Postgresql 9.1.

+1

См. Http://stackoverflow.com/search?q=%5Bpostgresql%5D%20upsert –

+0

@CraigRinger: Спасибо, попробуем это.Но все еще ищете подтверждение того, что несколько параметризованных вызовов процедуры не могут быть загружены при использовании JDBC + Postgres. Я видел метод addBatch для интерфейса Statement, и он делает что-то похожее на то, что я хочу, но не принимает параметры. Нужно сериализовать параметры в коде приложения и предоставить строку SQL. Существует много параметров, и каждый из них может быть подвержен ошибкам. – Dojo

+0

Не могли бы вы показать нам, как вы вызываете функцию из Java? – MatheusOl

ответ

1

Не знаю много о Postgres, в частности, но мой обычный метод заключается в следующем:

  • сделать вставку
  • если он терпит неудачу с unique_violation, сделать обновление

I делайте это так, чтобы минимизировать блокировки на столе и защищать от условий гонки.

Конечно, это будет работать, только если таблица, в которую вы вставляете, имеет уникальное ограничение на столбцы, отличные от первичного ключа.

7

Пакетный upsert делается в PostgreSQL по:

  • Начало транзакции
  • Создание TEMPORARY таблицы
  • Наполнение его с помощью JDBC порционный INSERT или (предпочтительно) путем использования COPY API, предоставленную PgJDBC водитель
  • LOCK В таблице реальных адресов IN EXCLUSIVE MODE, что позволяет использовать только SELECT s из других транзакций.
  • Ведение UPDATE ... FROM обновить существующие строки
  • Ведение INSERT INTO ... SELECT ... WHERE NOT EXISTS (SELECT 1 FROM real_table WHERE ...) добавить строки, которые уже не в real_table
  • COMMIT ИНГ сделки

Если несколько транзакций пытаются сделать это, они будут землю чтобы получить сериализацию на блокировке стола. Ускорение никогда не будет благоприятным для параллелизма.

Смежные вопросы